diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..d8901ee28 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,31 @@ +# EmmyLuaCodeStyle +[*.lua] +max_line_length = 26000 +indent_style = space +insert_final_newline = true +space_around_table_field_list = false +space_before_attribute = false +space_before_function_call_open_parenthesis = false +space_before_closure_open_parenthesis = false +space_before_function_call_single_arg = false +space_before_open_square_bracket = false +space_inside_function_call_parentheses = false +space_inside_function_param_list_parentheses = false +space_inside_square_brackets = false +space_around_table_append_operator = false +space_before_inline_comment = false +space_around_math_operator = false +space_around_logical_operator = false +space_around_assign_operator = false +space_after_comma = false +space_after_comma_in_for_statement = false +space_around_concat_operator = false +align_call_args = false +align_function_params = false +align_continuous_assign_statement = true +align_continuous_rect_table_field = true +align_if_branch = false +align_array_table = true +ignore_spaces_inside_function_call = true +line_space_after_function_statement = keep +trailing_table_separator = smart diff --git a/.github/500stars/README.md b/.github/500stars/README.md new file mode 100644 index 000000000..9ad3d6d12 --- /dev/null +++ b/.github/500stars/README.md @@ -0,0 +1,16 @@ +# Techmino - 500-star Banner +Created by NOT_A_ROBOT +13 September, 2024 + +**Don't forget to attribute me when using this.** +The image already includes sufficient attribution, so if you just don't crop that out, you shouldn't need to explicitly mention them. + +## Attribution +Created by NOT_A_ROBOT +GitHub logo (on Z-character's screen) by GitHub +Background (space stars) originally by MrZ, ported to JS by NOT_A_ROBOT for rendering +Block skin (featured in the background) by Scf, slightly modified to make it darker +Z-character drawn by 葉枭, designed by MrZ +Techmino by MrZ and many contributors + +Techmino is fun! https://github.com/26F-Studio/Techmino diff --git a/.github/500stars/exported.png b/.github/500stars/exported.png new file mode 100644 index 000000000..e3751e680 Binary files /dev/null and b/.github/500stars/exported.png differ diff --git a/.github/actions/get-cc/action.yml b/.github/actions/get-cc/action.yml deleted file mode 100644 index caf789e3b..000000000 --- a/.github/actions/get-cc/action.yml +++ /dev/null @@ -1,32 +0,0 @@ -name: 'get cc' -description: 'download cc into specific dir' -inputs: - tag: - required: false - default: "11.4" - platform: - required: true - dir: - required: false - default: '.' - repo: - required: false - default: 26F-Studio/cold_clear_ai_love2d_wrapper - temp-file: - required: false - default: temp.zip -runs: - using: "composite" - steps: - - run: | - echo "::set-output name=tag::"$(if [ -z "${{ inputs.tag }}" ] - then curl -w '%{url_effective}' -I -L -s -S https://github.com/${{ inputs.repo }}/releases/latest -o /dev/null | grep -o '\<[^/]*$' - else echo ${{ inputs.tag }} - fi) - id: get-tag - shell: bash - - uses: ./.github/actions/get-unzip - with: - url: https://github.com/${{ inputs.repo }}/releases/download/${{ steps.get-tag.outputs.tag }}/${{ inputs.platform }}.zip - dir: ${{ inputs.dir }} - temp-file: ${{ inputs.temp-file }} diff --git a/.github/build/android/dev/res/icon-playstore.png b/.github/build/android/dev/res/icon-playstore.png index db9d09224..8c7eb6709 100644 Binary files a/.github/build/android/dev/res/icon-playstore.png and b/.github/build/android/dev/res/icon-playstore.png differ diff --git a/.github/build/android/dev/res/mipmap-hdpi/icon.png b/.github/build/android/dev/res/mipmap-hdpi/icon.png index f355645b3..47002e9c6 100644 Binary files a/.github/build/android/dev/res/mipmap-hdpi/icon.png and b/.github/build/android/dev/res/mipmap-hdpi/icon.png differ diff --git a/.github/build/android/dev/res/mipmap-hdpi/icon_background.png b/.github/build/android/dev/res/mipmap-hdpi/icon_background.png index 0a212c9cb..6f05b8ad6 100644 Binary files a/.github/build/android/dev/res/mipmap-hdpi/icon_background.png and b/.github/build/android/dev/res/mipmap-hdpi/icon_background.png differ diff --git a/.github/build/android/dev/res/mipmap-hdpi/icon_foreground.png b/.github/build/android/dev/res/mipmap-hdpi/icon_foreground.png index e09b9a4e1..2ae7cd22b 100644 Binary files a/.github/build/android/dev/res/mipmap-hdpi/icon_foreground.png and b/.github/build/android/dev/res/mipmap-hdpi/icon_foreground.png differ diff --git a/.github/build/android/dev/res/mipmap-hdpi/icon_round.png b/.github/build/android/dev/res/mipmap-hdpi/icon_round.png index f70ee81e4..a74a1029e 100644 Binary files a/.github/build/android/dev/res/mipmap-hdpi/icon_round.png and b/.github/build/android/dev/res/mipmap-hdpi/icon_round.png differ diff --git a/.github/build/android/dev/res/mipmap-mdpi/icon.png b/.github/build/android/dev/res/mipmap-mdpi/icon.png index 6021f04a9..e2803d9fa 100644 Binary files a/.github/build/android/dev/res/mipmap-mdpi/icon.png and b/.github/build/android/dev/res/mipmap-mdpi/icon.png differ diff --git a/.github/build/android/dev/res/mipmap-mdpi/icon_background.png b/.github/build/android/dev/res/mipmap-mdpi/icon_background.png index a961ca12b..08b119c9a 100644 Binary files a/.github/build/android/dev/res/mipmap-mdpi/icon_background.png and b/.github/build/android/dev/res/mipmap-mdpi/icon_background.png differ diff --git a/.github/build/android/dev/res/mipmap-mdpi/icon_foreground.png b/.github/build/android/dev/res/mipmap-mdpi/icon_foreground.png index b48065554..e0b0176a9 100644 Binary files a/.github/build/android/dev/res/mipmap-mdpi/icon_foreground.png and b/.github/build/android/dev/res/mipmap-mdpi/icon_foreground.png differ diff --git a/.github/build/android/dev/res/mipmap-mdpi/icon_round.png b/.github/build/android/dev/res/mipmap-mdpi/icon_round.png index 5b80f445a..2d145b854 100644 Binary files a/.github/build/android/dev/res/mipmap-mdpi/icon_round.png and b/.github/build/android/dev/res/mipmap-mdpi/icon_round.png differ diff --git a/.github/build/android/dev/res/mipmap-xhdpi/icon.png b/.github/build/android/dev/res/mipmap-xhdpi/icon.png index aaad54e64..c56d513a5 100644 Binary files a/.github/build/android/dev/res/mipmap-xhdpi/icon.png and b/.github/build/android/dev/res/mipmap-xhdpi/icon.png differ diff --git a/.github/build/android/dev/res/mipmap-xhdpi/icon_background.png b/.github/build/android/dev/res/mipmap-xhdpi/icon_background.png index f644dec4d..39f0f544a 100644 Binary files a/.github/build/android/dev/res/mipmap-xhdpi/icon_background.png and b/.github/build/android/dev/res/mipmap-xhdpi/icon_background.png differ diff --git a/.github/build/android/dev/res/mipmap-xhdpi/icon_foreground.png b/.github/build/android/dev/res/mipmap-xhdpi/icon_foreground.png index a0a86e8fb..ef0dcd6b9 100644 Binary files a/.github/build/android/dev/res/mipmap-xhdpi/icon_foreground.png and b/.github/build/android/dev/res/mipmap-xhdpi/icon_foreground.png differ diff --git a/.github/build/android/dev/res/mipmap-xhdpi/icon_round.png b/.github/build/android/dev/res/mipmap-xhdpi/icon_round.png index 758807bca..dd43ba714 100644 Binary files a/.github/build/android/dev/res/mipmap-xhdpi/icon_round.png and b/.github/build/android/dev/res/mipmap-xhdpi/icon_round.png differ diff --git a/.github/build/android/dev/res/mipmap-xxhdpi/icon.png b/.github/build/android/dev/res/mipmap-xxhdpi/icon.png index 50b2e0a82..6a0d67876 100644 Binary files a/.github/build/android/dev/res/mipmap-xxhdpi/icon.png and b/.github/build/android/dev/res/mipmap-xxhdpi/icon.png differ diff --git a/.github/build/android/dev/res/mipmap-xxhdpi/icon_background.png b/.github/build/android/dev/res/mipmap-xxhdpi/icon_background.png index 5937963db..663c585a8 100644 Binary files a/.github/build/android/dev/res/mipmap-xxhdpi/icon_background.png and b/.github/build/android/dev/res/mipmap-xxhdpi/icon_background.png differ diff --git a/.github/build/android/dev/res/mipmap-xxhdpi/icon_foreground.png b/.github/build/android/dev/res/mipmap-xxhdpi/icon_foreground.png index 89629566b..7b6a2c087 100644 Binary files a/.github/build/android/dev/res/mipmap-xxhdpi/icon_foreground.png and b/.github/build/android/dev/res/mipmap-xxhdpi/icon_foreground.png differ diff --git a/.github/build/android/dev/res/mipmap-xxhdpi/icon_round.png b/.github/build/android/dev/res/mipmap-xxhdpi/icon_round.png index 7e0f8100b..482bd717d 100644 Binary files a/.github/build/android/dev/res/mipmap-xxhdpi/icon_round.png and b/.github/build/android/dev/res/mipmap-xxhdpi/icon_round.png differ diff --git a/.github/build/android/dev/res/mipmap-xxxhdpi/icon.png b/.github/build/android/dev/res/mipmap-xxxhdpi/icon.png index ad14c4590..3deefaa45 100644 Binary files a/.github/build/android/dev/res/mipmap-xxxhdpi/icon.png and b/.github/build/android/dev/res/mipmap-xxxhdpi/icon.png differ diff --git a/.github/build/android/dev/res/mipmap-xxxhdpi/icon_background.png b/.github/build/android/dev/res/mipmap-xxxhdpi/icon_background.png index c9674ee27..2ae710fa3 100644 Binary files a/.github/build/android/dev/res/mipmap-xxxhdpi/icon_background.png and b/.github/build/android/dev/res/mipmap-xxxhdpi/icon_background.png differ diff --git a/.github/build/android/dev/res/mipmap-xxxhdpi/icon_foreground.png b/.github/build/android/dev/res/mipmap-xxxhdpi/icon_foreground.png index f67b32c64..4257fcf70 100644 Binary files a/.github/build/android/dev/res/mipmap-xxxhdpi/icon_foreground.png and b/.github/build/android/dev/res/mipmap-xxxhdpi/icon_foreground.png differ diff --git a/.github/build/android/dev/res/mipmap-xxxhdpi/icon_round.png b/.github/build/android/dev/res/mipmap-xxxhdpi/icon_round.png index d280f49e3..671afb0eb 100644 Binary files a/.github/build/android/dev/res/mipmap-xxxhdpi/icon_round.png and b/.github/build/android/dev/res/mipmap-xxxhdpi/icon_round.png differ diff --git a/.github/build/android/release/res/icon-playstore.png b/.github/build/android/release/res/icon-playstore.png index 8c7eb6709..db9d09224 100644 Binary files a/.github/build/android/release/res/icon-playstore.png and b/.github/build/android/release/res/icon-playstore.png differ diff --git a/.github/build/android/release/res/mipmap-hdpi/icon.png b/.github/build/android/release/res/mipmap-hdpi/icon.png index 47002e9c6..f355645b3 100644 Binary files a/.github/build/android/release/res/mipmap-hdpi/icon.png and b/.github/build/android/release/res/mipmap-hdpi/icon.png differ diff --git a/.github/build/android/release/res/mipmap-hdpi/icon_background.png b/.github/build/android/release/res/mipmap-hdpi/icon_background.png index 6f05b8ad6..0a212c9cb 100644 Binary files a/.github/build/android/release/res/mipmap-hdpi/icon_background.png and b/.github/build/android/release/res/mipmap-hdpi/icon_background.png differ diff --git a/.github/build/android/release/res/mipmap-hdpi/icon_foreground.png b/.github/build/android/release/res/mipmap-hdpi/icon_foreground.png index 2ae7cd22b..e09b9a4e1 100644 Binary files a/.github/build/android/release/res/mipmap-hdpi/icon_foreground.png and b/.github/build/android/release/res/mipmap-hdpi/icon_foreground.png differ diff --git a/.github/build/android/release/res/mipmap-hdpi/icon_round.png b/.github/build/android/release/res/mipmap-hdpi/icon_round.png index a74a1029e..f70ee81e4 100644 Binary files a/.github/build/android/release/res/mipmap-hdpi/icon_round.png and b/.github/build/android/release/res/mipmap-hdpi/icon_round.png differ diff --git a/.github/build/android/release/res/mipmap-mdpi/icon.png b/.github/build/android/release/res/mipmap-mdpi/icon.png index e2803d9fa..6021f04a9 100644 Binary files a/.github/build/android/release/res/mipmap-mdpi/icon.png and b/.github/build/android/release/res/mipmap-mdpi/icon.png differ diff --git a/.github/build/android/release/res/mipmap-mdpi/icon_background.png b/.github/build/android/release/res/mipmap-mdpi/icon_background.png index 08b119c9a..a961ca12b 100644 Binary files a/.github/build/android/release/res/mipmap-mdpi/icon_background.png and b/.github/build/android/release/res/mipmap-mdpi/icon_background.png differ diff --git a/.github/build/android/release/res/mipmap-mdpi/icon_foreground.png b/.github/build/android/release/res/mipmap-mdpi/icon_foreground.png index e0b0176a9..b48065554 100644 Binary files a/.github/build/android/release/res/mipmap-mdpi/icon_foreground.png and b/.github/build/android/release/res/mipmap-mdpi/icon_foreground.png differ diff --git a/.github/build/android/release/res/mipmap-mdpi/icon_round.png b/.github/build/android/release/res/mipmap-mdpi/icon_round.png index 2d145b854..5b80f445a 100644 Binary files a/.github/build/android/release/res/mipmap-mdpi/icon_round.png and b/.github/build/android/release/res/mipmap-mdpi/icon_round.png differ diff --git a/.github/build/android/release/res/mipmap-xhdpi/icon.png b/.github/build/android/release/res/mipmap-xhdpi/icon.png index c56d513a5..aaad54e64 100644 Binary files a/.github/build/android/release/res/mipmap-xhdpi/icon.png and b/.github/build/android/release/res/mipmap-xhdpi/icon.png differ diff --git a/.github/build/android/release/res/mipmap-xhdpi/icon_background.png b/.github/build/android/release/res/mipmap-xhdpi/icon_background.png index 39f0f544a..f644dec4d 100644 Binary files a/.github/build/android/release/res/mipmap-xhdpi/icon_background.png and b/.github/build/android/release/res/mipmap-xhdpi/icon_background.png differ diff --git a/.github/build/android/release/res/mipmap-xhdpi/icon_foreground.png b/.github/build/android/release/res/mipmap-xhdpi/icon_foreground.png index ef0dcd6b9..a0a86e8fb 100644 Binary files a/.github/build/android/release/res/mipmap-xhdpi/icon_foreground.png and b/.github/build/android/release/res/mipmap-xhdpi/icon_foreground.png differ diff --git a/.github/build/android/release/res/mipmap-xhdpi/icon_round.png b/.github/build/android/release/res/mipmap-xhdpi/icon_round.png index dd43ba714..758807bca 100644 Binary files a/.github/build/android/release/res/mipmap-xhdpi/icon_round.png and b/.github/build/android/release/res/mipmap-xhdpi/icon_round.png differ diff --git a/.github/build/android/release/res/mipmap-xxhdpi/icon.png b/.github/build/android/release/res/mipmap-xxhdpi/icon.png index 6a0d67876..50b2e0a82 100644 Binary files a/.github/build/android/release/res/mipmap-xxhdpi/icon.png and b/.github/build/android/release/res/mipmap-xxhdpi/icon.png differ diff --git a/.github/build/android/release/res/mipmap-xxhdpi/icon_background.png b/.github/build/android/release/res/mipmap-xxhdpi/icon_background.png index 663c585a8..5937963db 100644 Binary files a/.github/build/android/release/res/mipmap-xxhdpi/icon_background.png and b/.github/build/android/release/res/mipmap-xxhdpi/icon_background.png differ diff --git a/.github/build/android/release/res/mipmap-xxhdpi/icon_foreground.png b/.github/build/android/release/res/mipmap-xxhdpi/icon_foreground.png index 7b6a2c087..89629566b 100644 Binary files a/.github/build/android/release/res/mipmap-xxhdpi/icon_foreground.png and b/.github/build/android/release/res/mipmap-xxhdpi/icon_foreground.png differ diff --git a/.github/build/android/release/res/mipmap-xxhdpi/icon_round.png b/.github/build/android/release/res/mipmap-xxhdpi/icon_round.png index 482bd717d..7e0f8100b 100644 Binary files a/.github/build/android/release/res/mipmap-xxhdpi/icon_round.png and b/.github/build/android/release/res/mipmap-xxhdpi/icon_round.png differ diff --git a/.github/build/android/release/res/mipmap-xxxhdpi/icon.png b/.github/build/android/release/res/mipmap-xxxhdpi/icon.png index 3deefaa45..ad14c4590 100644 Binary files a/.github/build/android/release/res/mipmap-xxxhdpi/icon.png and b/.github/build/android/release/res/mipmap-xxxhdpi/icon.png differ diff --git a/.github/build/android/release/res/mipmap-xxxhdpi/icon_background.png b/.github/build/android/release/res/mipmap-xxxhdpi/icon_background.png index 2ae710fa3..c9674ee27 100644 Binary files a/.github/build/android/release/res/mipmap-xxxhdpi/icon_background.png and b/.github/build/android/release/res/mipmap-xxxhdpi/icon_background.png differ diff --git a/.github/build/android/release/res/mipmap-xxxhdpi/icon_foreground.png b/.github/build/android/release/res/mipmap-xxxhdpi/icon_foreground.png index 4257fcf70..f67b32c64 100644 Binary files a/.github/build/android/release/res/mipmap-xxxhdpi/icon_foreground.png and b/.github/build/android/release/res/mipmap-xxxhdpi/icon_foreground.png differ diff --git a/.github/build/android/release/res/mipmap-xxxhdpi/icon_round.png b/.github/build/android/release/res/mipmap-xxxhdpi/icon_round.png index 671afb0eb..d280f49e3 100644 Binary files a/.github/build/android/release/res/mipmap-xxxhdpi/icon_round.png and b/.github/build/android/release/res/mipmap-xxxhdpi/icon_round.png differ diff --git a/.github/build/extraLibs/Windows_x64/discord-rpc.dll b/.github/build/extraLibs/Windows_x64/discord-rpc.dll new file mode 100644 index 000000000..8493c5490 Binary files /dev/null and b/.github/build/extraLibs/Windows_x64/discord-rpc.dll differ diff --git a/.github/build/extraLibs/Windows_x86/discord-rpc.dll b/.github/build/extraLibs/Windows_x86/discord-rpc.dll new file mode 100644 index 000000000..8493c5490 Binary files /dev/null and b/.github/build/extraLibs/Windows_x86/discord-rpc.dll differ diff --git a/.github/build/iOS/love.patch b/.github/build/iOS/love.patch index bbeec4d20..701b560da 100644 --- a/.github/build/iOS/love.patch +++ b/.github/build/iOS/love.patch @@ -8,7 +8,7 @@ index c1932555..552e432e 100644 **/ -void vibrate(); +void vibrate(const double seconds); - + /** * Enable mix mode (e.g. with background music apps) and playback with a muted device. diff --git a/src/common/ios.mm b/src/common/ios.mm @@ -18,16 +18,16 @@ index 7730991e..4ba8e708 100644 @@ -36,6 +36,8 @@ #include #include - + +#include + static NSArray *getLovesInDocuments(); static bool deleteFileInDocuments(NSString *filename); - + @@ -391,10 +393,40 @@ std::string getExecutablePath() } } - + -void vibrate() +void vibrate(const double seconds) { @@ -73,7 +73,7 @@ index c8af8596..ae7a5e32 100644 @@ -140,6 +140,10 @@ enum DoneAction DONE_RESTART, }; - + +extern "C" { + int luaopen_CCloader(lua_State *L); +} @@ -84,7 +84,7 @@ index c8af8596..ae7a5e32 100644 @@ -158,6 +162,9 @@ static DoneAction runlove(int argc, char **argv, int &retval) lua_State *L = luaL_newstate(); luaL_openlibs(L); - + + // Init CCloader + luaopen_CCloader(L); + diff --git a/.github/build/linux/dev/template.desktop b/.github/build/linux/dev/template.desktop deleted file mode 100644 index 31b0f21e2..000000000 --- a/.github/build/linux/dev/template.desktop +++ /dev/null @@ -1,10 +0,0 @@ -[Desktop Entry] -Name=Techmino Development -Comment=Techmino is fun! -MimeType=application/x-love-game; -Exec=app %f -Type=Application -Categories=Game; -Terminal=false -Icon=icon -NoDisplay=false \ No newline at end of file diff --git a/.github/build/linux/release/.gitattributes b/.github/build/linux/release/.gitattributes deleted file mode 100644 index c179c3203..000000000 --- a/.github/build/linux/release/.gitattributes +++ /dev/null @@ -1 +0,0 @@ -*.template text eol=lf diff --git a/.github/build/linux/release/template.desktop b/.github/build/linux/release/template.desktop deleted file mode 100644 index 11f27a4db..000000000 --- a/.github/build/linux/release/template.desktop +++ /dev/null @@ -1,10 +0,0 @@ -[Desktop Entry] -Name=Techmino -Comment=Techmino is fun! -MimeType=application/x-love-game; -Exec=app %f -Type=Application -Categories=Game; -Terminal=false -Icon=icon -NoDisplay=false \ No newline at end of file diff --git a/.github/build/web/dev/favicon.ico b/.github/build/web/dev/favicon.ico new file mode 100644 index 000000000..deabb9cc2 Binary files /dev/null and b/.github/build/web/dev/favicon.ico differ diff --git a/.github/build/web/release/favicon.ico b/.github/build/web/release/favicon.ico new file mode 100644 index 000000000..40793b9d8 Binary files /dev/null and b/.github/build/web/release/favicon.ico differ diff --git a/.github/build/windows/dev/template.rc b/.github/build/windows/dev/template.rc index 8aa567fb6..f1774d581 100644 --- a/.github/build/windows/dev/template.rc +++ b/.github/build/windows/dev/template.rc @@ -8,9 +8,12 @@ FILETYPE 0x1 { BLOCK "040904B0" { - VALUE "FileDescription", "Techmino Development" VALUE "CompanyName", "26F Studio" - VALUE "LegalCopyright", "Copyright @ 26F Studio" + VALUE "FileDescription", "Techmino Development" + VALUE "FileVersion", "@Version" + VALUE "InternalName", "Techmino" + VALUE "LegalCopyright", "Copyright © 2019-2023 26F-Studio. Some Rights Reserved." + VALUE "OriginalFilename", "Techmino.exe" VALUE "ProductName", "Techmino" VALUE "ProductVersion", "@Version" } diff --git a/.github/build/windows/release/template.rc b/.github/build/windows/release/template.rc index 14f7ff77c..1e94e1dd9 100644 --- a/.github/build/windows/release/template.rc +++ b/.github/build/windows/release/template.rc @@ -8,9 +8,12 @@ FILETYPE 0x1 { BLOCK "040904B0" { - VALUE "FileDescription", "Techmino" VALUE "CompanyName", "26F Studio" - VALUE "LegalCopyright", "Copyright @ 26F Studio" + VALUE "FileDescription", "Techmino" + VALUE "FileVersion", "@Version" + VALUE "InternalName", "Techmino" + VALUE "LegalCopyright", "Copyright © 2019-2023 26F-Studio. Some Rights Reserved." + VALUE "OriginalFilename", "Techmino.exe" VALUE "ProductName", "Techmino" VALUE "ProductVersion", "@Version" } diff --git a/.github/donate/donate.png b/.github/donate/donate.png new file mode 100644 index 000000000..d7462656c Binary files /dev/null and b/.github/donate/donate.png differ diff --git a/.github/workflows/dev.yml b/.github/workflows/dev.yml deleted file mode 100644 index 3cf96dabf..000000000 --- a/.github/workflows/dev.yml +++ /dev/null @@ -1,505 +0,0 @@ -name: Techmino Develop CI - -on: - push: - branches: [main, ci*] - pull_request: - branches: [main] -env: - CORE_LOVE_PACKAGE_PATH: ./core.love - CORE_LOVE_ARTIFACT_NAME: core_love_package -jobs: - get-info: - runs-on: ubuntu-latest - outputs: - app-name: ${{ steps.app-info.outputs.app-name }} - version-name: ${{ steps.app-info.outputs.version-name }} - version-string: ${{ steps.app-info.outputs.version-string }} - version-code: ${{ steps.app-info.outputs.version-code }} - commit-hash: ${{ steps.git-info.outputs.commit-hash }} - base-name: ${{ steps.assemble-base-name.outputs.base-name }} - steps: - - uses: actions/checkout@v3 - - name: Install lua - run: | - sudo apt-get install lua5.3 -y - - name: Get app info - id: app-info - shell: lua {0} - run: | - local version = require "version" - print("::set-output name=app-name::Techmino") - print("::set-output name=version-name::"..version.name) - print("::set-output name=version-string::"..version.string:gsub("%a", "")) - print(("::set-output name=version-code::%d"):format(version.code)) - - name: Get git info - id: git-info - shell: bash - run: | - COMMIT_HASH=$(git rev-parse --short ${{ GITHUB.SHA }}) - echo ::set-output name=commit-hash::$COMMIT_HASH - - name: Assemble package base name - id: assemble-base-name - shell: bash - run: | - BASE_NAME=Techmino_${{ steps.app-info.outputs.version-string }}_${{ steps.git-info.outputs.commit-hash }}_#${{ GITHUB.RUN_NUMBER }} - echo ::set-output name=base-name::$BASE_NAME - build-core: - runs-on: ubuntu-latest - needs: get-info - env: - PACKAGE_NAME: ./${{ needs.get-info.outputs.base-name }}_Core.love - outputs: - download-url: ${{ steps.transfer.outputs.download-url }} - steps: - - uses: actions/checkout@v3 - with: - submodules: recursive - - name: Build core love package - uses: 26F-Studio/love-actions-core@v1 - with: - build-list: ./media/ ./parts/ ./Zframework/ ./conf.lua ./main.lua ./version.lua - package-path: ${{ env.CORE_LOVE_PACKAGE_PATH }} - - name: Upload core love package - uses: actions/upload-artifact@v3 - with: - name: ${{ env.CORE_LOVE_ARTIFACT_NAME }} - path: ${{ env.CORE_LOVE_PACKAGE_PATH }} - - name: Rename love package - run: mv ${{ env.CORE_LOVE_PACKAGE_PATH }} ${{ env.PACKAGE_NAME }} - - name: Upload artifact - uses: actions/upload-artifact@v3 - with: - name: ${{ needs.get-info.outputs.base-name }}_Core - path: ${{ env.PACKAGE_NAME }} - - name: Upload to WeTransfer - id: transfer - run: | - curl -sL https://git.io/file-transfer | sh - ./transfer wet -s -p 16 --no-progress ${{ env.PACKAGE_NAME }} 2>&1>./wetransfer.log - echo "::set-output name=download-url::$(cat ./wetransfer.log | grep https | cut -f3 -d" ")" - auto-test: - runs-on: ubuntu-latest - needs: build-core - steps: - - uses: actions/checkout@v3 - with: - submodules: recursive - - name: Love actions for testing - uses: 26F-Studio/love-actions-test@v1 - with: - font-path: ./parts/fonts/proportional.otf - language-folder: ./parts/language - build-android: - runs-on: ubuntu-latest - needs: [get-info, build-core, auto-test] - env: - OUTPUT_FOLDER: ./build - outputs: - download-url: ${{ steps.transfer.outputs.download-url }} - steps: - - uses: actions/checkout@v3 - with: - submodules: recursive - - name: Process app name - id: process-app-name - shell: python3 {0} - run: | - import os - import re - os.system("echo ::set-output name=bundle-id::org.f26_studio." + re.sub(r"[^A-Za-z0-9]+", "_", "${{ needs.get-info.outputs.app-name }}")) - os.system("echo ::set-output name=product-name::" + re.sub(r"[^A-Za-z0-9]+", "-", "${{ needs.get-info.outputs.app-name }}")) - - name: Download core love package - uses: actions/download-artifact@v3 - with: - name: ${{ env.CORE_LOVE_ARTIFACT_NAME }} - - name: Download ColdClear - uses: ./.github/actions/get-cc - with: - platform: Android - dir: ./ColdClear - - name: Process ColdClear - shell: bash - run: | - mkdir -p ./libAndroid/armeabi-v7a/ - mkdir -p ./libAndroid/arm64-v8a/ - mv ./ColdClear/armeabi-v7a/libCCloader.so ./libAndroid/armeabi-v7a/ - mv ./ColdClear/arm64-v8a/libCCloader.so ./libAndroid/arm64-v8a/ - - name: Build Android packages - id: build-packages - uses: 26F-Studio/love-actions-android@v1 - with: - app-name: ${{ needs.get-info.outputs.app-name }} - bundle-id: ${{ steps.process-app-name.outputs.bundle-id }} - icon-specifier: "@mipmap/icon" - keystore-alias: ${{ secrets.ANDROID_KEYSTORE_ALIAS }} - keystore-base64: ${{ secrets.ANDROID_KEYSTORE_BASE64 }} - keystore-key-password: ${{ secrets.ANDROID_KEYSTORE_KEYPASSWORD }} - keystore-store-password: ${{ secrets.ANDROID_KEYSTORE_STOREPASSWORD }} - love-package: ${{ env.CORE_LOVE_PACKAGE_PATH }} - resource-path: ./.github/build/android/dev/res - libs-path: ./ColdClear/ - extra-assets: ./libAndroid/ - product-name: ${{ steps.process-app-name.outputs.product-name }} - version-string: ${{ needs.get-info.outputs.version-string }} - version-code: ${{ needs.get-info.outputs.version-code }} - output-folder: ${{ env.OUTPUT_FOLDER }} - - name: Upload artifact - uses: actions/upload-artifact@v3 - with: - name: ${{ needs.get-info.outputs.base-name }}_Android - path: ${{ env.OUTPUT_FOLDER }}/* - - name: Upload to WeTransfer - id: transfer - run: | - curl -sL https://git.io/file-transfer | sh - ./transfer wet -s -p 16 --no-progress ${{ steps.build-packages.outputs.package-paths }} 2>&1>./wetransfer.log - echo "::set-output name=download-url::$(cat ./wetransfer.log | grep https | cut -f3 -d" ")" - build-ios: - runs-on: macos-latest - needs: [get-info, build-core, auto-test] - env: - OUTPUT_FOLDER: ./build - outputs: - download-url: ${{ steps.transfer.outputs.download-url }} - steps: - - uses: actions/checkout@v3 - with: - submodules: recursive - - name: Process app name - id: process-app-name - shell: python3 {0} - run: | - import os - import re - os.system("echo ::set-output name=bundle-id::org.26f-studio.techmino") - os.system("echo ::set-output name=product-name::" + re.sub(r"[^A-Za-z0-9]+", "_", "${{ needs.get-info.outputs.app-name }}")) - - name: Download core love package - uses: actions/download-artifact@v3 - with: - name: ${{ env.CORE_LOVE_ARTIFACT_NAME }} - - name: Download ColdClear - uses: ./.github/actions/get-cc - with: - platform: iOS - dir: ./ColdClear - - name: Build iOS packages - id: build-packages - uses: 26F-Studio/love-actions-ios@v1 - with: - app-name: ${{ needs.get-info.outputs.app-name }} - bundle-id: ${{ steps.process-app-name.outputs.bundle-id }} - copyright: "Copyright © 2019-2022 26F-Studio. Some Rights Reserved." - icon-path: ./.github/build/iOS/dev/icon - love-patch: ./.github/build/iOS/love.patch - love-package: ${{ env.CORE_LOVE_PACKAGE_PATH }} - libs-path: ./ColdClear/arm64/ - product-name: ${{ steps.process-app-name.outputs.product-name }} - version-string: ${{ needs.get-info.outputs.version-string }} - output-folder: ${{ env.OUTPUT_FOLDER }} - apple-development-base64: ${{ secrets.APPLE_CERT_APPLE_DEVELOPMENT_BASE64 }} - apple-development-password: ${{ secrets.APPLE_CERT_APPLE_DEVELOPMENT_PWD }} - api-key: ${{ secrets.APPLE_API_KEY }} - api-key-id: ${{ secrets.APPLE_API_KEY_ID }} - api-issuer-id: ${{ secrets.APPLE_API_ISSUER_ID }} - team-id: ${{ secrets.APPLE_DEVELOPER_TEAM_ID }} - apple-id: ${{ secrets.APPLE_APPLE_ID }} - - name: Upload artifact - uses: actions/upload-artifact@v3 - with: - name: ${{ needs.get-info.outputs.base-name }}_iOS - path: ${{ env.OUTPUT_FOLDER }}/* - - name: Upload to WeTransfer - id: transfer - run: | - wget -qO- https://github.com/Mikubill/transfer/releases/download/v0.4.17/transfer_0.4.17_darwin_amd64.tar.gz | tar xvz - ./transfer wet -s -p 16 --no-progress ${{ steps.build-packages.outputs.package-paths }} 2>&1>./wetransfer.log - echo "::set-output name=download-url::$(cat ./wetransfer.log | grep https | cut -f3 -d" ")" - build-linux: - runs-on: ubuntu-latest - needs: [get-info, build-core, auto-test] - env: - OUTPUT_FOLDER: ./build - outputs: - download-url: ${{ steps.transfer.outputs.download-url }} - steps: - - uses: actions/checkout@v3 - with: - submodules: recursive - - name: Process app name - id: process-app-name - shell: python3 {0} - run: | - import os - import re - os.system("echo ::set-output name=product-name::" + re.sub(r"[^A-Za-z0-9]+", "_", "${{ needs.get-info.outputs.app-name }}")) - - name: Download core love package - uses: actions/download-artifact@v3 - with: - name: ${{ env.CORE_LOVE_ARTIFACT_NAME }} - - name: Download ColdClear - uses: ./.github/actions/get-cc - with: - platform: Linux - dir: ./ColdClear - - name: Process ColdClear - shell: bash - run: | - cd ./ColdClear - mkdir ./libs - mv ./x64/libcold_clear.so ./libs - mkdir ./shared - mv ./x64/CCloader.so ./shared - - name: Build Linux packages - id: build-packages - uses: 26F-Studio/love-actions-linux@v1 - with: - desktop-file-path: ./.github/build/linux/dev/template.desktop - executable-name: app - icon-path: ./.github/build/linux/dev/icon.png - love-package: ${{ env.CORE_LOVE_PACKAGE_PATH }} - libs-path: ./ColdClear/libs/ - shared-path: ./ColdClear/shared/ - product-name: ${{ steps.process-app-name.outputs.product-name }} - output-folder: ${{ env.OUTPUT_FOLDER }} - - name: Upload artifact - uses: actions/upload-artifact@v3 - with: - name: ${{ needs.get-info.outputs.base-name }}_Linux - path: ${{ env.OUTPUT_FOLDER }}/* - - name: Upload to WeTransfer - id: transfer - run: | - curl -sL https://git.io/file-transfer | sh - ./transfer wet -s -p 16 --no-progress ${{ steps.build-packages.outputs.package-paths }} 2>&1>./wetransfer.log - echo "::set-output name=download-url::$(cat ./wetransfer.log | grep https | cut -f3 -d" ")" - build-macos-appstore: - runs-on: macos-latest - needs: [get-info, build-core, auto-test] - env: - OUTPUT_FOLDER: ./build - outputs: - download-url: ${{ steps.transfer.outputs.download-url }} - steps: - - uses: actions/checkout@v3 - with: - submodules: recursive - - name: Process app name - id: process-app-name - shell: python3 {0} - run: | - import os - import re - os.system("echo ::set-output name=bundle-id::org.26f-studio.techmino") - os.system("echo ::set-output name=product-name::" + re.sub(r"[^A-Za-z0-9]+", "_", "${{ needs.get-info.outputs.app-name }}")) - - name: Download core love package - uses: actions/download-artifact@v3 - with: - name: ${{ env.CORE_LOVE_ARTIFACT_NAME }} - - name: Download ColdClear - uses: ./.github/actions/get-cc - with: - platform: macOS - dir: ./ColdClear - - name: Process ColdClear - shell: bash - run: | - rm ./ColdClear/universal/libcold_clear.a - - name: Build macOS packages - id: build-packages - uses: 26F-Studio/love-actions-macos-appstore@v1 - with: - app-name: ${{ needs.get-info.outputs.app-name }} - bundle-id: ${{ steps.process-app-name.outputs.bundle-id }} - copyright: "Copyright © 2019-2022 26F-Studio. Some Rights Reserved." - icon-path: ./.github/build/macOS/dev/icon.icns - love-package: ${{ env.CORE_LOVE_PACKAGE_PATH }} - libs-path: ./ColdClear/universal/ - product-name: ${{ steps.process-app-name.outputs.product-name }} - version-string: ${{ needs.get-info.outputs.version-string }} - output-folder: ${{ env.OUTPUT_FOLDER }} - apple-development-base64: ${{ secrets.APPLE_CERT_APPLE_DEVELOPMENT_BASE64 }} - apple-development-password: ${{ secrets.APPLE_CERT_APPLE_DEVELOPMENT_PWD }} - api-key: ${{ secrets.APPLE_API_KEY }} - api-key-id: ${{ secrets.APPLE_API_KEY_ID }} - api-issuer-id: ${{ secrets.APPLE_API_ISSUER_ID }} - team-id: ${{ secrets.APPLE_DEVELOPER_TEAM_ID }} - apple-id: ${{ secrets.APPLE_APPLE_ID }} - - name: Upload artifact - uses: actions/upload-artifact@v3 - with: - name: ${{ needs.get-info.outputs.base-name }}_macOS_appstore - path: ${{ env.OUTPUT_FOLDER }}/* - - name: Upload to WeTransfer - id: transfer - run: | - wget -qO- https://github.com/Mikubill/transfer/releases/download/v0.4.17/transfer_0.4.17_darwin_amd64.tar.gz | tar xvz - ./transfer wet -s -p 16 --no-progress ${{ steps.build-packages.outputs.package-paths }} 2>&1>./wetransfer.log - echo "::set-output name=download-url::$(cat ./wetransfer.log | grep https | cut -f3 -d" ")" - build-macos-portable: - runs-on: macos-latest - needs: [get-info, build-core, auto-test] - env: - OUTPUT_FOLDER: ./build - outputs: - download-url: ${{ steps.transfer.outputs.download-url }} - steps: - - uses: actions/checkout@v3 - with: - submodules: recursive - - name: Process app name - id: process-app-name - shell: python3 {0} - run: | - import os - import re - os.system("echo ::set-output name=bundle-id::org.26f-studio.techmino") - os.system("echo ::set-output name=product-name::" + re.sub(r"[^A-Za-z0-9]+", "_", "${{ needs.get-info.outputs.app-name }}")) - - name: Download core love package - uses: actions/download-artifact@v3 - with: - name: ${{ env.CORE_LOVE_ARTIFACT_NAME }} - - name: Download ColdClear - uses: ./.github/actions/get-cc - with: - platform: macOS - dir: ./ColdClear - - name: Process ColdClear - shell: bash - run: | - rm ./ColdClear/universal/libcold_clear.a - - name: Build macOS packages - id: build-packages - uses: 26F-Studio/love-actions-macos-portable@v1 - with: - app-name: ${{ needs.get-info.outputs.app-name }} - bundle-id: ${{ steps.process-app-name.outputs.bundle-id }} - copyright: "Copyright © 2019-2022 26F-Studio. Some Rights Reserved." - icon-path: ./.github/build/macOS/dev/icon.icns - love-package: ${{ env.CORE_LOVE_PACKAGE_PATH }} - libs-path: ./ColdClear/universal/ - product-name: ${{ steps.process-app-name.outputs.product-name }} - version-string: ${{ needs.get-info.outputs.version-string }} - output-folder: ${{ env.OUTPUT_FOLDER }} - account-username: ${{ secrets.APPLE_ACCOUNT_USERNAME }} - account-password: ${{ secrets.APPLE_ACCOUNT_PASSWORD }} - team-id: "${{ secrets.APPLE_DEVELOPER_TEAM_ID }}" - developer-id-application-base64: ${{ secrets.APPLE_CERT_DEVELOPER_ID_APPLICATION }} - developer-id-application-password: ${{ secrets.APPLE_CERT_DEVELOPER_ID_APPLICATION_PWD }} - developer-id-installer-base64: ${{ secrets.APPLE_CERT_DEVELOPER_ID_INSTALLER }} - developer-id-installer-password: ${{ secrets.APPLE_CERT_DEVELOPER_ID_INSTALLER_PWD }} - dmg-background-path: ./.github/build/macOS/dev/dmg.png - dmg-icon-position: "239 203" - dmg-icon-size: "100" - dmg-link-position: "565 203" - dmg-text-size: "12" - dmg-volume-icon-path: ./.github/build/macOS/dev/dmg.icns - dmg-volume-name: ${{ steps.process-app-name.outputs.product-name }} - dmg-window-position: "200 120" - dmg-window-size: "800 500" - - name: Upload artifact - uses: actions/upload-artifact@v3 - with: - name: ${{ needs.get-info.outputs.base-name }}_macOS_portable - path: ${{ env.OUTPUT_FOLDER }}/* - - name: Upload to WeTransfer - id: transfer - run: | - wget -qO- https://github.com/Mikubill/transfer/releases/download/v0.4.17/transfer_0.4.17_darwin_amd64.tar.gz | tar xvz - ./transfer wet -s -p 16 --no-progress ${{ steps.build-packages.outputs.package-paths }} 2>&1>./wetransfer.log - echo "::set-output name=download-url::$(cat ./wetransfer.log | grep https | cut -f3 -d" ")" - build-windows: - runs-on: windows-latest - needs: [get-info, build-core, auto-test] - env: - OUTPUT_FOLDER: ./build - outputs: - download-url: ${{ steps.transfer.outputs.download-url }} - steps: - - uses: actions/checkout@v3 - with: - submodules: recursive - - name: Process app name - id: process-app-name - shell: python3 {0} - run: | - import os - import re - os.system("echo ::set-output name=product-name::" + re.sub(r"[^A-Za-z0-9]+", "_", "${{ needs.get-info.outputs.app-name }}")) - - name: Download core love package - uses: actions/download-artifact@v3 - with: - name: ${{ env.CORE_LOVE_ARTIFACT_NAME }} - - name: Build Windows packages - id: build-packages - uses: 26F-Studio/love-actions-windows@v1 - with: - icon-path: ./.github/build/windows/dev/icon.ico - rc-path: ./.github/build/windows/dev/template.rc - love-package: ${{ env.CORE_LOVE_PACKAGE_PATH }} - product-name: ${{ steps.process-app-name.outputs.product-name }} - version-string: ${{ needs.get-info.outputs.version-string }} - output-folder: ${{ env.OUTPUT_FOLDER }} - - name: Upload artifact - uses: actions/upload-artifact@v3 - with: - name: ${{ needs.get-info.outputs.base-name }}_Windows - path: ${{ env.OUTPUT_FOLDER }}/* - - name: Get transfer - env: - TEMP_PATH: ./temp.zip - shell: bash - run: | - curl -L --retry 5 https://github.com/Mikubill/transfer/releases/download/v0.4.17/transfer_0.4.17_windows_amd64.zip -o ${{ env.TEMP_PATH }} - 7z x ${{ env.TEMP_PATH }} -o./ - rm ${{ env.TEMP_PATH }} - - name: Upload to WeTransfer - id: transfer - run: | - ./transfer wet -s -p 16 --no-progress ${{ steps.build-packages.outputs.package-paths }} 2>&1>./wetransfer.log - echo "::set-output name=download-url::$(cat ./wetransfer.log | grep https | cut -f3 -d" ")" - post-build: - runs-on: ubuntu-latest - if: ${{ always() }} - needs: - [ - get-info, - auto-test, - build-core, - build-android, - build-ios, - build-linux, - build-macos-appstore, - build-macos-portable, - build-windows, - ] - steps: - - uses: actions/checkout@v3 - - name: Cleanup - uses: geekyeggo/delete-artifact@v1 - with: - name: ${{ env.CORE_LOVE_ARTIFACT_NAME }} - - name: Send Discord message - uses: Sniddl/discord-commits@v1.3 - with: - webhook: ${{ secrets.DISCORD_WEBHOOK }} - message: "Github Actions for **${{ github.repository }}**." - embed: '{ - "author":{ - "name":"${{ needs.get-info.outputs.app-name }} [Development]", - "url":"https://github.com/${{ github.repository }}" - }, - "title":"${{ needs.get-info.outputs.app-name }} (${{ needs.get-info.outputs.version-name }}) Build Result", - "description": "CI triggered at ${{ needs.get-info.outputs.commit-hash }}", - "url":"https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}", - "thumbnail":{ - "url":"https://raw.githubusercontent.com/${{ github.repository }}/main/.github/build/linux/dev/icon.png" - }, - "color":36863, - "fields":[ - {"name":"Version","value":"${{ needs.get-info.outputs.version-string }}","inline": true}, - {"name":"Package Name","value":"${{ needs.get-info.outputs.base-name }}","inline": true}, - {"name":"Status","value":"**Automatic Test:** ${{ needs.auto-test.result }}\n**Core:** ${{ needs.build-core.result }}\n**Android:** ${{ needs.build-android.result }}\n**iOS:** ${{ needs.build-ios.result }}\n**Linux:** ${{ needs.build-linux.result }}\n**macOS App Store:** ${{ needs.build-macos-appstore.result }}\n**macOS portable:** ${{ needs.build-macos-portable.result }}\n**Windows:** ${{ needs.build-windows.result }}"}, - {"name":"Download Links","value":"**Core:** ${{ needs.build-core.outputs.download-url}}\n**Android:** ${{ needs.build-android.outputs.download-url }}\n**iOS:** ${{ needs.build-ios.outputs.download-url }}\n**Linux:** ${{ needs.build-linux.outputs.download-url }}\n**macOS App Store:** ${{ needs.build-macos-appstore.outputs.download-url }}\n**macOS portable:** ${{ needs.build-macos-portable.outputs.download-url }}\n**Windows:** ${{ needs.build-windows.outputs.download-url}}"} - ] - }' diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 000000000..89af07d5d --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,523 @@ +name: Techmino CI + +on: + push: + branches: [main, ci*] + tags: [pre*, v*] + pull_request: + branches: [main] + +env: + BUILD_ASSETS_FOLDER: ./.github/build + BUILD_TYPE: ${{ fromJSON('["dev", "release"]')[startsWith(github.ref, 'refs/tags/v')] }} + COLD_CLEAR_DOWNLOAD_URL: https://github.com/26F-Studio/cold_clear_ai_love2d_wrapper/releases/download/11.5 + CORE_LOVE_ARTIFACT_NAME: core_love_package + CORE_LOVE_PACKAGE_PATH: ./core.love + +jobs: + get-info: + runs-on: ubuntu-latest + outputs: + app-name: ${{ steps.app-info.outputs.app-name }} + version-name: ${{ steps.app-info.outputs.version-name }} + version-string: ${{ steps.app-info.outputs.version-string }} + version-code: ${{ steps.app-info.outputs.version-code }} + update-title: ${{ steps.app-info.outputs.update-title }} + update-note: ${{ steps.app-info.outputs.update-note }} + commit-hash: ${{ steps.git-info.outputs.commit-hash }} + base-name: ${{ steps.assemble-base-name.outputs.base-name }} + steps: + - uses: actions/checkout@v4 + - name: Install lua + run: | + sudo apt-get install lua5.3 -y + - name: Get app info + id: app-info + shell: lua {0} + run: | + local version = require "version" + os.execute('echo "app-name=Techmino" >> $GITHUB_OUTPUT') + os.execute('echo "version-name=' .. version.name .. '" >> $GITHUB_OUTPUT') + os.execute('echo "version-string=' .. version.string:gsub("%a", "") .. '" >> $GITHUB_OUTPUT') + os.execute('echo "version-code=' .. tostring(version.code) .. '" >> $GITHUB_OUTPUT') + + local f = io.open("updateLog.txt", 'r') + local note = f:read("*a") + f:close() + local p1 = note:find("\n%d") + 1 + local p2 = note:find("\n", p1) - 1 + os.execute('echo "update-title=' .. note:sub(p1, p2) .. '" >> $GITHUB_OUTPUT') + local p3 = note:find("\n", note:find("\n%d") + 1) + 1 + local p4 = note:find("\n%d", p3 + 1) + updateNote = note:sub(p3, p4 - 2) + :gsub(" ", "- ") + :gsub(" ", "# ") + os.execute('echo "update-note<> $GITHUB_OUTPUT') + os.execute('echo "' .. updateNote .. '" >> $GITHUB_OUTPUT') + os.execute('echo "EOF" >> $GITHUB_OUTPUT') + - name: Get git info + id: git-info + shell: bash + run: | + COMMIT_HASH=$(git rev-parse --short ${{ GITHUB.SHA }}) + echo "commit-hash=$COMMIT_HASH" >> $GITHUB_OUTPUT + - name: Assemble package base name + id: assemble-base-name + shell: bash + run: | + BASE_NAME=Techmino_${{ steps.app-info.outputs.version-string }}_${{ steps.git-info.outputs.commit-hash }}_#${{ GITHUB.RUN_NUMBER }} + echo "base-name=$BASE_NAME" >> $GITHUB_OUTPUT + + build-core: + runs-on: ubuntu-latest + needs: get-info + env: + OUTPUT_FOLDER: ./build + RELEASE_FOLDER: ./release + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + - name: Process app name + id: process-app-name + shell: python3 {0} + run: | + import os + import re + with open(os.getenv('GITHUB_OUTPUT'), 'a') as f: + f.write('product-name=' + re.sub(r'[^A-Za-z0-9]+', '_', '${{ needs.get-info.outputs.app-name }}') + '\n') + - uses: ./.github/actions/update-version + if: ${{ !startsWith(github.ref, 'refs/tags/v') }} + with: + commit: ${{ needs.get-info.outputs.commit-hash }} + type: snapshot + - name: Build core love package + uses: love-actions/love-actions-core@v1 + with: + build-list: ./media/ ./parts/ ./Zframework/ ./conf.lua ./main.lua ./version.lua ./legals.md ./license.txt + package-path: ${{ env.CORE_LOVE_PACKAGE_PATH }} + - name: Upload core love package + uses: actions/upload-artifact@v4 + with: + name: ${{ env.CORE_LOVE_ARTIFACT_NAME }} + path: ${{ env.CORE_LOVE_PACKAGE_PATH }} + - name: Rename love package + run: | + mkdir -p ${{ env.OUTPUT_FOLDER }} + mv ${{ env.CORE_LOVE_PACKAGE_PATH }} ${{ env.OUTPUT_FOLDER }}/${{ steps.process-app-name.outputs.product-name }}.love + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: ${{ needs.get-info.outputs.base-name }}_Core_love + path: ${{ env.OUTPUT_FOLDER }}/${{ steps.process-app-name.outputs.product-name }}.love + - name: Prepare for release + if: ${{ startsWith(github.ref, 'refs/tags/pre') || startsWith(github.ref, 'refs/tags/v') }} + shell: bash + run: | + mkdir -p ${{ env.RELEASE_FOLDER }} + cp ${{ env.OUTPUT_FOLDER }}/${{ steps.process-app-name.outputs.product-name }}.love ${{ env.RELEASE_FOLDER }}/${{ steps.process-app-name.outputs.product-name }}_Bare.love + - name: Upload release + if: ${{ startsWith(github.ref, 'refs/tags/pre') || startsWith(github.ref, 'refs/tags/v') }} + uses: ncipollo/release-action@v1 + with: + allowUpdates: true + artifacts: ${{ env.RELEASE_FOLDER }}/${{ steps.process-app-name.outputs.product-name }}_Bare.love + body: ${{ needs.get-info.outputs.update-note }} + name: ${{ needs.get-info.outputs.update-title }} + prerelease: ${{ startsWith(github.ref, 'refs/tags/pre') }} + + auto-test: + runs-on: ubuntu-24.04 + needs: build-core + env: + APPIMAGE_PATH: ./love.AppImage + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + - name: Love actions for testing + uses: love-actions/love-actions-test@v1 + with: + font-path: ./parts/fonts/proportional.otf + language-folder: ./parts/language + - name: Download core love package + uses: actions/download-artifact@v4 + with: + name: ${{ env.CORE_LOVE_ARTIFACT_NAME }} + - name: Download love + shell: bash + run: | + curl --retry 5 https://github.com/love2d/love/releases/download/11.5/love-11.5-x86_64.AppImage -o ${{ env.APPIMAGE_PATH }} + chmod +x ${{ env.APPIMAGE_PATH }} + - name: Install dependencies + shell: bash + run: | + sudo apt-get update + sudo apt-get install alsa-oss alsa-utils libfuse2 pavucontrol pulseaudio pulseaudio-utils x11-xserver-utils xvfb -y + - name: Run automated test + shell: bash + run: | + xvfb-run --auto-servernum ${{ env.APPIMAGE_PATH }} ${{ env.CORE_LOVE_PACKAGE_PATH }} --test + + build-android: + runs-on: ubuntu-latest + needs: [get-info, build-core, auto-test] + if: github.event_name != 'pull_request' + env: + COLD_CLEAR_FOLDER: ./libAndroid + OUTPUT_FOLDER: ./build + RELEASE_FOLDER: ./release + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + - name: Process app name + id: process-app-name + shell: python3 {0} + run: | + import os + import re + with open(os.getenv('GITHUB_OUTPUT'), 'a') as f: + if "${{ env.BUILD_TYPE }}" == "dev": + f.write('bundle-id=org.f26_studio.' + re.sub(r'[^A-Za-z0-9]+', '_', '${{ needs.get-info.outputs.app-name }}') + '.snapshot\n') + f.write('product-name=' + re.sub(r'[^A-Za-z0-9]+', '-', '${{ needs.get-info.outputs.app-name }}') + '_Snapshot\n') + else: + f.write('bundle-id=org.f26_studio.' + re.sub(r'[^A-Za-z0-9]+', '_', '${{ needs.get-info.outputs.app-name }}') + '\n') + f.write('product-name=' + re.sub(r'[^A-Za-z0-9]+', '-', '${{ needs.get-info.outputs.app-name }}') + '\n') + - name: Download core love package + uses: actions/download-artifact@v4 + with: + name: ${{ env.CORE_LOVE_ARTIFACT_NAME }} + - name: Download ColdClear + uses: ./.github/actions/get-unzip + with: + url: ${{ env.COLD_CLEAR_DOWNLOAD_URL }}/Android.zip + dir: ${{ env.COLD_CLEAR_FOLDER }} + - name: Build Android packages + id: build-packages + uses: love-actions/love-actions-android@v2 + with: + app-name: ${{ needs.get-info.outputs.app-name }} + bundle-id: ${{ steps.process-app-name.outputs.bundle-id }} + icon-specifier: "@mipmap/icon" + keystore-alias: ${{ secrets.ANDROID_KEYSTORE_ALIAS }} + keystore-base64: ${{ secrets.ANDROID_KEYSTORE_BASE64 }} + keystore-key-password: ${{ secrets.ANDROID_KEYSTORE_KEYPASSWORD }} + keystore-store-password: ${{ secrets.ANDROID_KEYSTORE_STOREPASSWORD }} + love-package: ${{ env.CORE_LOVE_PACKAGE_PATH }} + resource-path: ${{ env.BUILD_ASSETS_FOLDER }}/android/${{ env.BUILD_TYPE }}/res + extra-assets: ${{ env.COLD_CLEAR_FOLDER }} + custom-scheme: studio26f://oauth + product-name: ${{ steps.process-app-name.outputs.product-name }} + version-string: ${{ needs.get-info.outputs.version-string }} + version-code: ${{ needs.get-info.outputs.version-code }} + output-folder: ${{ env.OUTPUT_FOLDER }} + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: ${{ needs.get-info.outputs.base-name }}_Android_release + path: ${{ env.OUTPUT_FOLDER }}/${{ steps.process-app-name.outputs.product-name }}-release.apk + - name: Prepare for release + if: ${{ startsWith(github.ref, 'refs/tags/pre') || startsWith(github.ref, 'refs/tags/v') }} + shell: bash + run: | + mkdir -p ${{ env.RELEASE_FOLDER }} + cp ${{ env.OUTPUT_FOLDER }}/${{ steps.process-app-name.outputs.product-name }}-release.apk ${{ env.RELEASE_FOLDER }}/${{ steps.process-app-name.outputs.product-name }}_Android.apk + - name: Upload release + if: ${{ startsWith(github.ref, 'refs/tags/pre') || startsWith(github.ref, 'refs/tags/v') }} + uses: ncipollo/release-action@v1 + with: + allowUpdates: true + artifacts: ${{ env.RELEASE_FOLDER }}/${{ steps.process-app-name.outputs.product-name }}_Android.apk + body: ${{ needs.get-info.outputs.update-note }} + name: ${{ needs.get-info.outputs.update-title }} + prerelease: ${{ startsWith(github.ref, 'refs/tags/pre') }} + + build-linux: + runs-on: ubuntu-latest + needs: [get-info, build-core, auto-test] + env: + COLD_CLEAR_FOLDER: ./ColdClear + OUTPUT_FOLDER: ./build + RELEASE_FOLDER: ./release + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + - name: Process app name + id: process-app-name + shell: python3 {0} + run: | + import os + import re + product_name = re.sub(r'[^A-Za-z0-9]+', '-', '${{ needs.get-info.outputs.app-name }}').strip('-').lower() + with open(os.getenv('GITHUB_OUTPUT'), 'a') as f: + f.write('bundle-id=org.26f-studio.' + product_name + '\n') + f.write('product-name=' + product_name + '\n') + - name: Download core love package + uses: actions/download-artifact@v4 + with: + name: ${{ env.CORE_LOVE_ARTIFACT_NAME }} + - name: Add icon to love package + run: | + cp ${{ env.BUILD_ASSETS_FOLDER }}/linux/${{ env.BUILD_TYPE }}/icon.png media/image/icon.png + zip -u ${{ env.CORE_LOVE_PACKAGE_PATH }} media/image/icon.png + rm media/image/icon.png + - name: Download ColdClear + uses: ./.github/actions/get-unzip + with: + url: ${{ env.COLD_CLEAR_DOWNLOAD_URL }}/Linux.zip + dir: ${{ env.COLD_CLEAR_FOLDER }} + - name: Process ColdClear + shell: bash + run: | + cd ${{ env.COLD_CLEAR_FOLDER }} + mkdir -p ./lib/lua/5.1 + mv ./x64/CCloader.so ./lib/lua/5.1 + - name: Build Linux packages + id: build-packages + uses: love-actions/love-actions-linux@v2 + with: + app-name: ${{ needs.get-info.outputs.app-name }} + bundle-id: ${{ steps.process-app-name.outputs.bundle-id }} + description: Techmino is fun! + version-string: ${{ needs.get-info.outputs.version-string }} + icon-path: ${{ env.BUILD_ASSETS_FOLDER }}/linux/${{ env.BUILD_TYPE }}/icon.png + love-package: ${{ env.CORE_LOVE_PACKAGE_PATH }} + lib-path: ${{ env.COLD_CLEAR_FOLDER }}/lib + product-name: ${{ steps.process-app-name.outputs.product-name }} + output-folder: ${{ env.OUTPUT_FOLDER }} + - name: Upload AppImage artifact + uses: actions/upload-artifact@v4 + with: + name: ${{ needs.get-info.outputs.base-name }}_Linux_AppImage + path: ${{ env.OUTPUT_FOLDER }}/${{ steps.process-app-name.outputs.product-name }}.AppImage + - name: Upload Debian artifact + uses: actions/upload-artifact@v4 + with: + name: ${{ needs.get-info.outputs.base-name }}_Linux_Debian + path: ${{ env.OUTPUT_FOLDER }}/${{ steps.process-app-name.outputs.product-name }}.deb + - name: Prepare for release + if: ${{ startsWith(github.ref, 'refs/tags/pre') || startsWith(github.ref, 'refs/tags/v') }} + shell: bash + run: | + mkdir -p ${{ env.RELEASE_FOLDER }} + cp ${{ env.OUTPUT_FOLDER }}/${{ steps.process-app-name.outputs.product-name }}.AppImage ${{ env.RELEASE_FOLDER }}/${{ steps.process-app-name.outputs.product-name }}_Linux.AppImage + cp ${{ env.OUTPUT_FOLDER }}/${{ steps.process-app-name.outputs.product-name }}.deb ${{ env.RELEASE_FOLDER }}/${{ steps.process-app-name.outputs.product-name }}_Linux.deb + - name: Upload release + if: ${{ startsWith(github.ref, 'refs/tags/pre') || startsWith(github.ref, 'refs/tags/v') }} + uses: ncipollo/release-action@v1 + with: + allowUpdates: true + artifacts: | + ${{ env.RELEASE_FOLDER }}/${{ steps.process-app-name.outputs.product-name }}_Linux.AppImage + ${{ env.RELEASE_FOLDER }}/${{ steps.process-app-name.outputs.product-name }}_Linux.deb + body: ${{ needs.get-info.outputs.update-note }} + name: ${{ needs.get-info.outputs.update-title }} + prerelease: ${{ startsWith(github.ref, 'refs/tags/pre') }} + + build-web-compat: + runs-on: ubuntu-latest + needs: [get-info, build-core, auto-test] + if: github.event_name != 'pull_request' + env: + MEMORY_LIMIT: 128000000 + OUTPUT_FOLDER: ./build + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + - name: Download core love package + uses: actions/download-artifact@v4 + with: + name: ${{ env.CORE_LOVE_ARTIFACT_NAME }} + - name: Build web packages + run: | + npx love.js ${{ env.CORE_LOVE_PACKAGE_PATH }} ${{ env.OUTPUT_FOLDER }} -t "${{ needs.get-info.outputs.app-name }}" -m ${{ env.MEMORY_LIMIT }} -c + - name: Move assets + run: | + mv ${{ env.BUILD_ASSETS_FOLDER }}/web/${{ env.BUILD_TYPE }}/* ${{ env.OUTPUT_FOLDER }} + - name: Initialize Love.js Api Player + env: + LOVEJS_API_PLAYER_BASE_URL: https://raw.githubusercontent.com/MrcSnm/Love.js-Api-Player/refs/heads/master + run: | + pushd ${{ env.OUTPUT_FOLDER }} + sed -i 's|| \n |' index.html + sed -i 's|| \n |' index.html + sed -i 's|| \n |' index.html + sed -i 's|32, 37, 38, 39, 40|37, 38, 39, 40|' index.html + wget ${{ env.LOVEJS_API_PLAYER_BASE_URL }}/consolewrapper.js + wget ${{ env.LOVEJS_API_PLAYER_BASE_URL }}/globalizeFS.js + wget ${{ env.LOVEJS_API_PLAYER_BASE_URL }}/webdb.js + node globalizeFS.js + rm globalizeFS.js + popd + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: ${{ needs.get-info.outputs.base-name }}_Web + path: ${{ env.OUTPUT_FOLDER }} + - name: Deploy to GitHub Pages + uses: crazy-max/ghaction-github-pages@v4 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + build_dir: ${{ env.OUTPUT_FOLDER }} + keep_history: false + target_branch: gh-pages + + build-windows: + runs-on: windows-latest + needs: [get-info, build-core, auto-test] + env: + COLD_CLEAR_FOLDER: ./ColdClear + OUTPUT_FOLDER: ./build + RELEASE_FOLDER: ./release + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + - name: Process app name + id: process-app-name + shell: python3 {0} + run: | + import os + import re + with open(os.getenv('GITHUB_OUTPUT'), 'a') as f: + f.write('product-name=' + re.sub(r'[^A-Za-z0-9]+', '_', '${{ needs.get-info.outputs.app-name }}') + '\n') + - name: Download core love package + uses: actions/download-artifact@v4 + with: + name: ${{ env.CORE_LOVE_ARTIFACT_NAME }} + - name: Download ColdClear + uses: ./.github/actions/get-unzip + with: + url: ${{ env.COLD_CLEAR_DOWNLOAD_URL }}/Windows.zip + dir: ${{ env.COLD_CLEAR_FOLDER }} + - name: Update Windows template + shell: python3 {0} + run: | + version_string = "${{ needs.get-info.outputs.version-string }}" + file_version = (f"{version_string.replace('.', ',')},0") + with open("${{ env.BUILD_ASSETS_FOLDER }}/windows/${{ env.BUILD_TYPE }}/template.rc", "r+", encoding="utf8") as file: + data = file.read() + data = data\ + .replace("@Version", version_string)\ + .replace("@FileVersion", file_version) + file.seek(0) + file.truncate() + file.write(data) + - name: Build Windows packages + id: build-packages + uses: love-actions/love-actions-windows@v2 + with: + icon-path: ${{ env.BUILD_ASSETS_FOLDER }}/windows/${{ env.BUILD_TYPE }}/icon.ico + rc-path: ${{ env.BUILD_ASSETS_FOLDER }}/windows/${{ env.BUILD_TYPE }}/template.rc + love-package: ${{ env.CORE_LOVE_PACKAGE_PATH }} + extra-assets-x86: ${{ env.COLD_CLEAR_FOLDER }}/x86/CCloader.dll ${{ env.COLD_CLEAR_FOLDER }}/x86/cold_clear.dll ${{ env.BUILD_ASSETS_FOLDER }}/extraLibs/Windows_x64/discord-rpc.dll + extra-assets-x64: ${{ env.COLD_CLEAR_FOLDER }}/x64/CCloader.dll ${{ env.COLD_CLEAR_FOLDER }}/x64/cold_clear.dll ${{ env.BUILD_ASSETS_FOLDER }}/extraLibs/Windows_x86/discord-rpc.dll + product-name: ${{ steps.process-app-name.outputs.product-name }} + app-id: ${{ secrets.WINDOWS_APP_ID }} + project-website: https://www.studio26f.org/ + installer-languages: ChineseSimplified.isl ChineseTraditional.isl English.isl Spanish.isl French.isl Indonesian.isl Japanese.isl Portuguese.isl + output-folder: ${{ env.OUTPUT_FOLDER }} + - name: Upload 32-bit artifact + uses: actions/upload-artifact@v4 + with: + name: ${{ needs.get-info.outputs.base-name }}_Windows_x86 + path: ${{ env.OUTPUT_FOLDER }}/${{ steps.process-app-name.outputs.product-name }}_x86.zip + - name: Upload 64-bit artifact + uses: actions/upload-artifact@v4 + with: + name: ${{ needs.get-info.outputs.base-name }}_Windows_x64 + path: ${{ env.OUTPUT_FOLDER }}/${{ steps.process-app-name.outputs.product-name }}_x64.zip + - name: Upload installer artifact + uses: actions/upload-artifact@v4 + with: + name: ${{ needs.get-info.outputs.base-name }}_Windows_installer + path: ${{ env.OUTPUT_FOLDER }}/${{ steps.process-app-name.outputs.product-name }}_installer.exe + - name: Prepare for release + if: ${{ startsWith(github.ref, 'refs/tags/pre') || startsWith(github.ref, 'refs/tags/v') }} + shell: bash + run: | + mkdir -p ${{ env.RELEASE_FOLDER }} + cp ${{ env.OUTPUT_FOLDER }}/${{ steps.process-app-name.outputs.product-name }}_x86.zip ${{ env.RELEASE_FOLDER }}/${{ steps.process-app-name.outputs.product-name }}_Windows_x86.zip + cp ${{ env.OUTPUT_FOLDER }}/${{ steps.process-app-name.outputs.product-name }}_x64.zip ${{ env.RELEASE_FOLDER }}/${{ steps.process-app-name.outputs.product-name }}_Windows_x64.zip + cp ${{ env.OUTPUT_FOLDER }}/${{ steps.process-app-name.outputs.product-name }}_installer.exe ${{ env.RELEASE_FOLDER }}/${{ steps.process-app-name.outputs.product-name }}_Windows_installer.exe + - name: Upload release + if: ${{ startsWith(github.ref, 'refs/tags/pre') || startsWith(github.ref, 'refs/tags/v') }} + uses: ncipollo/release-action@v1 + with: + allowUpdates: true + artifacts: | + ${{ env.RELEASE_FOLDER }}/${{ steps.process-app-name.outputs.product-name }}_Windows_x86.zip + ${{ env.RELEASE_FOLDER }}/${{ steps.process-app-name.outputs.product-name }}_Windows_x64.zip + ${{ env.RELEASE_FOLDER }}/${{ steps.process-app-name.outputs.product-name }}_Windows_installer.exe + body: ${{ needs.get-info.outputs.update-note }} + name: ${{ needs.get-info.outputs.update-title }} + prerelease: ${{ startsWith(github.ref, 'refs/tags/pre') }} + + post-build: + runs-on: ubuntu-latest + if: ${{ always() }} + needs: + [ + get-info, + auto-test, + build-core, + build-android, + build-linux, + build-web-compat, + build-windows, + ] + env: + ACTION_TYPE: ${{ fromJSON('[["Development", "Pre-release"], ["Release", "Release"]]')[startsWith(github.ref, 'refs/tags/v')][startsWith(github.ref, 'refs/tags/pre')] }} + steps: + - name: Cleanup + uses: geekyeggo/delete-artifact@v5 + with: + name: ${{ env.CORE_LOVE_ARTIFACT_NAME }} + - name: Send Discord message + if: github.ref == 'refs/heads/main' + shell: python + run: | + import requests + import json + headers = { + 'Content-Type': 'application/json', + } + payload = json.dumps({ + "content": "Github Actions for **${{ github.repository }}**.", + "embeds": [ + { + "author": { + "name": "${{ needs.get-info.outputs.app-name }} [${{ env.ACTION_TYPE }}]", + "url": "https://github.com/${{ github.repository }}" + }, + "title": "${{ needs.get-info.outputs.app-name }} (${{ needs.get-info.outputs.version-name }}) Build Result", + "description": "CI triggered at ${{ needs.get-info.outputs.commit-hash }}", + "url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}", + "thumbnail": { + "url": "https://raw.githubusercontent.com/${{ github.repository }}/main/.github/build/linux/${{ env.BUILD_TYPE }}/icon.png" + }, + "color": 36863, + "fields": [ + { + "name": "Version", + "value": "${{ needs.get-info.outputs.version-string }}", + "inline": True + }, + { + "name": "Package Name", + "value": "${{ needs.get-info.outputs.base-name }}", + "inline": True + }, + { + "name": "Status", + "value": "**Automatic Test:** ${{ needs.auto-test.result }}\n**Core:** ${{ needs.build-core.result }}\n**Android:** ${{ needs.build-android.result }}\n**Linux:** ${{ needs.build-linux.result }}\n**Web compatible:** ${{ needs.build-web-compat.result }}\n**Windows:** ${{ needs.build-windows.result }}" + } + ] + } + ] + }) + requests.request("POST", "${{ secrets.DISCORD_WEBHOOK }}", headers=headers, data=payload) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml deleted file mode 100644 index c164ff250..000000000 --- a/.github/workflows/release.yml +++ /dev/null @@ -1,215 +0,0 @@ -name: Techmino Release CI - -on: - push: - tags: - - "v*" - -jobs: - get-info: - runs-on: ubuntu-20.04 - outputs: - name: ${{ steps.lua-related.outputs.name }} - apkCode: ${{ steps.lua-related.outputs.apkCode }} - code: ${{ steps.lua-related.outputs.code }} - release: ${{ steps.lua-related.outputs.release }} - updateTitle: ${{ steps.lua-related.outputs.updateTitle }} - updateNote: ${{ steps.lua-related.outputs.updateNote }} - commit: ${{ steps.git-related.outputs.commit }} - steps: - - uses: actions/checkout@v2 - - name: Install lua - run: | - sudo apt-get install lua5.3 -y - - name: Get Version - id: lua-related - shell: lua {0} - run: | - local version = require 'version' - print("::set-output name=name::"..version.string) - print(("::set-output name=apkCode::%d"):format(version.apkCode)) - print(("::set-output name=code::%d"):format(version.code)) - print("::set-output name=release::"..version.string:gsub("V", "", 1)) - - local note = require 'parts.updateLog' - local p1 = note:find("\n%d") + 1 - local p2 = note:find("\n", p1) - 1 - print("::set-output name=updateTitle::"..note:sub(p1, p2)) - - local p3 = note:find("\n", note:find("\n%d") + 1) + 1 - local p4 = note:find("\n%d", p3 + 1) - print("::set-output name=updateNote::"..note:sub(p3, p4 - 2) - :gsub(" ", "- ") - :gsub(" ", "# ") - :gsub('%%', '%%25') - :gsub('\n', '%%0A') - :gsub('\r', '%%0D')) - - name: Get Commit - id: git-related - shell: bash - run: | - echo "::set-output name=commit::$(git rev-parse --short ${{ GITHUB.SHA }})" - - build-windows-x64: - runs-on: windows-latest - needs: get-info - steps: - - uses: actions/checkout@v2 - with: - submodules: 'recursive' - - uses: ./.github/actions/update-version - - uses: ./.github/actions/build-windows - with: - love-url: https://github.com/love2d/love/releases/download/11.3/love-11.3-win64.zip - love-dir: love-11.3-win64 - arch: win64 - version: ${{ needs.get-info.outputs.release }} - icon: .\.github\build\Windows\icon.ico - - name: Pack Techmino - run: 7z a -tzip .\Techmino_a${{ needs.get-info.outputs.release }}_Win64.zip .\love - - name: Release - uses: softprops/action-gh-release@v1 - with: - name: ${{ needs.get-info.outputs.updateTitle }} - files: Techmino_a${{ needs.get-info.outputs.release }}_Win64.zip - - build-windows-x86: - runs-on: windows-latest - needs: get-info - steps: - - uses: actions/checkout@v2 - with: - submodules: 'recursive' - - uses: ./.github/actions/update-version - - uses: ./.github/actions/build-windows - with: - love-url: https://github.com/love2d/love/releases/download/11.3/love-11.3-win32.zip - love-dir: love-11.3-win32 - arch: win32 - version: ${{ needs.get-info.outputs.release }} - icon: .\.github\build\Windows\icon.ico - - name: Pack Techmino - run: 7z a -tzip .\Techmino_a${{ needs.get-info.outputs.release }}_Win32.zip .\love - - name: Release - uses: softprops/action-gh-release@v1 - with: - name: ${{ needs.get-info.outputs.updateTitle }} - files: Techmino_a${{ needs.get-info.outputs.release }}_Win32.zip - - build-linux: - runs-on: ubuntu-20.04 - needs: get-info - steps: - - uses: actions/checkout@v2 - with: - submodules: 'recursive' - - uses: ./.github/actions/update-version - - uses: ./.github/actions/build-linux - with: - file-path: Techmino_a${{ needs.get-info.outputs.release }}_Linux.AppImage - icon: .github/build/Linux/icon.png - - name: Release - uses: softprops/action-gh-release@v1 - with: - name: ${{ needs.get-info.outputs.updateTitle }} - files: Techmino_a${{ needs.get-info.outputs.release }}_Linux.AppImage - - build-android: - runs-on: ubuntu-20.04 - needs: get-info - steps: - - uses: actions/checkout@v2 - with: - submodules: 'recursive' - - uses: ./.github/actions/update-version - - uses: ./.github/actions/build-android - with: - type: Release - apkCode: ${{ needs.get-info.outputs.apkCode }} - name: ${{ needs.get-info.outputs.name }} - file-path: Techmino_a${{ needs.get-info.outputs.release }}_Android.apk - SIGNING_KEY: ${{ secrets.SIGNING_KEY }} - KEY_STORE_PASSWORD: ${{ secrets.KEY_STORE_PASSWORD }} - ALIAS: ${{ secrets.ALIAS }} - KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }} - - name: Release - uses: softprops/action-gh-release@v1 - with: - name: ${{ needs.get-info.outputs.updateTitle }} - files: Techmino_a${{ needs.get-info.outputs.release }}_Android.apk - - build-macOS: - runs-on: macos-10.15 - needs: get-info - steps: - - uses: actions/checkout@v2 - with: - submodules: 'recursive' - - uses: ./.github/actions/update-version - - uses: ./.github/actions/build-macos - with: - name: ${{ needs.get-info.outputs.name }} - icon: .github/build/macOS/icon.icns - APPLE_API_ID: "${{ secrets.APPLE_API_ID }}" - APPLE_API_ISSUER: "${{ secrets.APPLE_API_ISSUER }}" - APPLE_API_KEY: "${{ secrets.APPLE_API_KEY }}" - APPLE_APP_IDENTIFIER: "${{ secrets.APPLE_APP_IDENTIFIER }}" - APPLE_KEYCHAIN_NAME: "${{ secrets.APPLE_KEYCHAIN_NAME }}" - APPLE_KEYCHAIN_PWD: "${{ secrets.APPLE_KEYCHAIN_PWD }}" - FASTLANE_MATCH_PWD: "${{ secrets.FASTLANE_MATCH_PWD }}" - FASTLANE_MATCH_TOKEN: "${{ secrets.FASTLANE_MATCH_TOKEN }}" - - name: Pack Techmino - run: | - mv Techmino.dmg Techmino_a${{ needs.get-info.outputs.release }}_MacOS.dmg - - name: Release - uses: softprops/action-gh-release@v1 - with: - name: ${{ needs.get-info.outputs.updateTitle }} - files: Techmino_a${{ needs.get-info.outputs.release }}_MacOS.dmg - - build-iOS: - runs-on: macos-latest - needs: get-info - steps: - - uses: actions/checkout@v2 - with: - submodules: 'recursive' - - uses: ./.github/actions/update-version - - uses: ./.github/actions/build-ios - with: - name: ${{ needs.get-info.outputs.name }} - type: "release" - APPLE_API_ID: "${{ secrets.APPLE_API_ID }}" - APPLE_API_ISSUER: "${{ secrets.APPLE_API_ISSUER }}" - APPLE_API_KEY: "${{ secrets.APPLE_API_KEY }}" - APPLE_APP_BUILD: "${{ needs.get-info.outputs.code }}.0.${{ github.run_number }}.${{ github.run_attempt }}" - APPLE_APP_CHANGELOG: "${{ needs.get-info.outputs.updateNote }}" - APPLE_APP_ID: "${{ secrets.APPLE_APP_ID }}" - APPLE_APP_IDENTIFIER: "${{ secrets.APPLE_APP_IDENTIFIER }}" - APPLE_APP_PROFILE: "${{ secrets.APPLE_APP_PROFILE }}" - APPLE_KEYCHAIN_NAME: "${{ secrets.APPLE_KEYCHAIN_NAME }}" - APPLE_KEYCHAIN_PWD: "${{ secrets.APPLE_KEYCHAIN_PWD }}" - FASTLANE_ACTION_ID: "${{ github.run_id }}" - FASTLANE_DISCORD_WEBHOOK: "${{ secrets.FASTLANE_DISCORD_WEBHOOK }}" - FASTLANE_MATCH_PWD: "${{ secrets.FASTLANE_MATCH_PWD }}" - FASTLANE_MATCH_TOKEN: "${{ secrets.FASTLANE_MATCH_TOKEN }}" - - name: Rename ipa - shell: bash - run: | - mv Techmino.ipa Techmino_a${{ needs.get-info.outputs.release }}_iOS.ipa - - name: Release - uses: softprops/action-gh-release@v1 - with: - name: ${{ needs.get-info.outputs.updateTitle }} - files: Techmino_a${{ needs.get-info.outputs.release }}_iOS.ipa - - Add-Release-note: - runs-on: ubuntu-20.04 - needs: get-info - steps: - - uses: actions/checkout@v2 - - name: Release - uses: softprops/action-gh-release@v1 - with: - name: ${{ needs.get-info.outputs.updateTitle }} - body: ${{ needs.get-info.outputs.updateNote }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml deleted file mode 100644 index 63cb8e3c7..000000000 --- a/.github/workflows/test.yml +++ /dev/null @@ -1,170 +0,0 @@ -name: Techmino Test CI - -on: - push: - tags: - - 'pre*' - -jobs: - get-info: - runs-on: ubuntu-20.04 - outputs: - name: ${{ steps.lua-related.outputs.name }} - apkCode: ${{ steps.lua-related.outputs.apkCode }} - code: ${{ steps.lua-related.outputs.code }} - release: ${{ steps.lua-related.outputs.release }} - commit: ${{ steps.git-related.outputs.commit }} - steps: - - uses: actions/checkout@v2 - - name: Install lua - run: | - sudo apt-get install lua5.3 -y - - name: Get Version - id: lua-related - shell: lua {0} - run: | - local version = require 'version' - print("::set-output name=name::"..version.string) - print(("::set-output name=apkCode::%d"):format(version.apkCode)) - print(("::set-output name=code::%d"):format(version.code)) - print("::set-output name=release::"..version.string:gsub("V","",1)) - - name: Get Commit - id: git-related - shell: bash - run: | - echo "::set-output name=commit::$(git rev-parse --short ${{ GITHUB.SHA }})" - - build-windows: - runs-on: windows-latest - needs: get-info - steps: - - uses: actions/checkout@v2 - with: - submodules: 'recursive' - - uses: ./.github/actions/update-version - with: - commit: ${{ needs.get-info.outputs.commit }} - type: snapshot - - uses: ./.github/actions/build-windows - with: - love-url: https://github.com/love2d/love/releases/download/11.3/love-11.3-win64.zip - love-dir: love-11.3-win64 - arch: win64 - version: ${{ needs.get-info.outputs.name }} - icon: .\.github\build\Windows\icon_snapshot.ico - - name: Upload - uses: actions/upload-artifact@v2 - with: - name: Techmino_pre${{ needs.get-info.outputs.release }}_${{ needs.get-info.outputs.commit }}_Windows - path: love - - build-linux: - runs-on: ubuntu-20.04 - needs: get-info - steps: - - uses: actions/checkout@v2 - with: - submodules: 'recursive' - - uses: ./.github/actions/update-version - with: - commit: ${{ needs.get-info.outputs.commit }} - type: snapshot - - uses: ./.github/actions/build-linux - with: - icon: .github/build/Linux/icon_snapshot.png - - name: Upload - uses: actions/upload-artifact@v2 - with: - name: Techmino_pre${{ needs.get-info.outputs.release }}_${{ needs.get-info.outputs.commit }}_Linux - path: Techmino.AppImage - - build-android: - runs-on: ubuntu-20.04 - needs: get-info - steps: - - uses: actions/checkout@v2 - with: - submodules: 'recursive' - - uses: ./.github/actions/update-version - with: - commit: ${{ needs.get-info.outputs.commit }} - type: snapshot - - uses: ./.github/actions/build-android - with: - type: Snapshot - apkCode: ${{ needs.get-info.outputs.apkCode }} - name: ${{ needs.get-info.outputs.name }} - file-path: Techmino_Snapshot.apk - SIGNING_KEY: ${{ secrets.SIGNING_KEY }} - KEY_STORE_PASSWORD: ${{ secrets.KEY_STORE_PASSWORD }} - ALIAS: ${{ secrets.ALIAS }} - KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }} - - name: Upload - uses: actions/upload-artifact@v2 - with: - name: Techmino_pre${{ needs.get-info.outputs.release }}_${{ needs.get-info.outputs.commit }}_Android - path: Techmino_Snapshot.apk - - build-macOS: - runs-on: macos-10.15 - needs: get-info - steps: - - uses: actions/checkout@v2 - with: - submodules: 'recursive' - - uses: ./.github/actions/update-version - with: - commit: ${{ needs.get-info.outputs.commit }} - type: snapshot - - uses: ./.github/actions/build-macos - with: - name: ${{ needs.get-info.outputs.name }} - icon: .github/build/macOS/icon_snapshot.icns - APPLE_API_ID: '${{ secrets.APPLE_API_ID }}' - APPLE_API_ISSUER: '${{ secrets.APPLE_API_ISSUER }}' - APPLE_API_KEY: '${{ secrets.APPLE_API_KEY }}' - APPLE_APP_IDENTIFIER: '${{ secrets.APPLE_APP_IDENTIFIER }}' - APPLE_KEYCHAIN_NAME: '${{ secrets.APPLE_KEYCHAIN_NAME }}' - APPLE_KEYCHAIN_PWD: '${{ secrets.APPLE_KEYCHAIN_PWD }}' - FASTLANE_MATCH_PWD: '${{ secrets.FASTLANE_MATCH_PWD }}' - FASTLANE_MATCH_TOKEN: '${{ secrets.FASTLANE_MATCH_TOKEN }}' - - name: Upload - uses: actions/upload-artifact@v2 - with: - name: Techmino_pre${{ needs.get-info.outputs.release }}_${{ needs.get-info.outputs.commit }}_MacOS - path: Techmino.dmg - - build-iOS: - runs-on: macos-latest - needs: get-info - steps: - - uses: actions/checkout@v2 - with: - submodules: 'recursive' - - uses: ./.github/actions/update-version - with: - commit: ${{ needs.get-info.outputs.commit }} - type: snapshot - - uses: ./.github/actions/build-ios - with: - name: ${{ needs.get-info.outputs.name }} - type: 'test' - APPLE_API_ID: '${{ secrets.APPLE_API_ID }}' - APPLE_API_ISSUER: '${{ secrets.APPLE_API_ISSUER }}' - APPLE_API_KEY: '${{ secrets.APPLE_API_KEY }}' - APPLE_APP_BUILD: '${{ needs.get-info.outputs.code }}.1.${{ github.run_number }}.${{ github.run_attempt }}' - APPLE_APP_CHANGELOG: '${{ github.event.commits[0].message }}' - APPLE_APP_ID: '${{ secrets.APPLE_APP_ID }}' - APPLE_APP_IDENTIFIER: '${{ secrets.APPLE_APP_IDENTIFIER }}' - APPLE_APP_PROFILE: '${{ secrets.APPLE_APP_PROFILE }}' - APPLE_KEYCHAIN_NAME: '${{ secrets.APPLE_KEYCHAIN_NAME }}' - APPLE_KEYCHAIN_PWD: '${{ secrets.APPLE_KEYCHAIN_PWD }}' - FASTLANE_ACTION_ID: '${{ github.run_id }}' - FASTLANE_DISCORD_WEBHOOK: '${{ secrets.FASTLANE_DISCORD_WEBHOOK }}' - FASTLANE_MATCH_PWD: '${{ secrets.FASTLANE_MATCH_PWD }}' - FASTLANE_MATCH_TOKEN: '${{ secrets.FASTLANE_MATCH_TOKEN }}' - - name: Upload - uses: actions/upload-artifact@v2 - with: - name: Techmino_pre${{ needs.get-info.outputs.release }}_${{ needs.get-info.outputs.commit }}_iOS - path: Techmino.ipa diff --git a/.gitignore b/.gitignore index be29303f9..4a7b924b2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,4 @@ .vscode -libAndroid *.ini .DS_Store Thumbs.db diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 9f049b305..000000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "Zframework"] - path = Zframework - url = https://github.com/26F-Studio/Zframework.git diff --git a/Zframework b/Zframework deleted file mode 160000 index d1d5f4fab..000000000 --- a/Zframework +++ /dev/null @@ -1 +0,0 @@ -Subproject commit d1d5f4fabc854d38dcebffe7863d8ca7d5813ede diff --git a/Zframework/background.lua b/Zframework/background.lua new file mode 100644 index 000000000..2622ba594 --- /dev/null +++ b/Zframework/background.lua @@ -0,0 +1,57 @@ +local gc_clear=love.graphics.clear +local BGs={ + none={draw=function() gc_clear(.08,.08,.084) end} +} +local BGlist={'none'} +local BG={ + default='none', + locked=false, + cur='none', + init=false, + resize=false, + update=NULL, + draw=BGs.none.draw, + event=false, + discard=NULL, +} + +function BG.lock() BG.locked=true end +function BG.unlock() BG.locked=false end +function BG.add(name,bg) + BGs[name]=bg + BGlist[#BGlist+1]=name +end +function BG.getList() + return BGlist +end +function BG.remList(name) + table.remove(BGlist,TABLE.find(BGlist,name)) +end +function BG.send(...) + if BG.event then + BG.event(...) + end +end +function BG.setDefault(bg) + BG.default=bg +end +function BG.set(name,...) + name=name or BG.default + if not BGs[name] or BG.locked then return end + if name~=BG.cur then + BG.discard() + BG.cur=name + local bg=BGs[name] + + BG.init= bg.init or NULL + BG.resize= bg.resize or NULL + BG.update= bg.update or NULL + BG.draw= bg.draw or NULL + BG.event= bg.event or NULL + BG.discard=bg.discard or NULL + BG.init() + if ... then BG.send(...) end + end + return true +end +return BG diff --git a/Zframework/bgm.lua b/Zframework/bgm.lua new file mode 100644 index 000000000..5b2e93143 --- /dev/null +++ b/Zframework/bgm.lua @@ -0,0 +1,350 @@ +local audio=love.audio +local effectsSupported=audio.isEffectsSupported() + +local nameList={} +local srcLib={}-- Stored bgm objects: {name='foo', source=bar, ...}, more info at function _addFile() +local lastLoadNames={} +local nowPlay={} +local lastPlay=NONE-- Directly stored last played bgm name(s) + +local defaultBGM=false +local maxLoadedCount=3 +local volume=1 + +local function task_setVolume(obj,ve,time,stop) + local vs=obj.vol + local t=0 + while true do + t=time~=0 and math.min(t+coroutine.yield()/time,1) or 1 + local v=MATH.mix(vs,ve,t) + obj.vol=v + obj.source:setVolume(v*volume) + if t==1 then + obj.volChanging=false + break + end + end + if stop then + obj.source:stop() + end + obj.volChanging=false + return true +end +local function task_setPitch(obj,pe,time) + local ps=obj.pitch + local t=0 + while true do + t=time~=0 and math.min(t+coroutine.yield()/time,1) or 1 + local p=MATH.mix(ps,pe,t) + obj.pitch=p + obj.source:setPitch(p) + if t==1 then + obj.pitchChanging=false + return true + end + end +end +local function task_setLowgain(obj,pe,time) + local ps=obj.lowgain + local t=0 + while true do + t=time~=0 and math.min(t+coroutine.yield()/time,1) or 1 + local p=MATH.mix(ps,pe,t) + obj.lowgain=p + obj.source:setFilter{type='bandpass',lowgain=obj.lowgain^9.42,highgain=obj.highgain^9.42,volume=1} + if t==1 then + obj.lowgainChanging=false + return true + end + end +end +local function task_setHighgain(obj,pe,time) + local ps=obj.highgain + local t=0 + while true do + t=time~=0 and math.min(t+coroutine.yield()/time,1) or 1 + local p=MATH.mix(ps,pe,t) + obj.highgain=p + obj.source:setFilter{type='bandpass',lowgain=obj.lowgain^9.42,highgain=obj.highgain^9.42,volume=1} + if t==1 then + obj.highgainChanging=false + return true + end + end +end +local function _clearTask(obj,mode) + local taskFunc= + mode=='volume' and task_setVolume or + mode=='pitch' and task_setPitch or + mode=='lowgain' and task_setLowgain or + mode=='highgain' and task_setHighgain or + 'any' + TASK.removeTask_iterate(function(task) + return task.args[1]==obj and (taskFunc=='any' or task.code==taskFunc) + end,obj) +end + +local function _updateSources() + local n=#lastLoadNames + while #lastLoadNames>maxLoadedCount and n>0 do + local name=lastLoadNames[n] + if srcLib[name].source and not srcLib[name].source:isPlaying() then + srcLib[name].source=srcLib[name].source:release() and nil + _clearTask(srcLib[name],'any') + end + n=n-1 + end +end +local function _addFile(name,path) + if not srcLib[name] then + table.insert(nameList,name) + srcLib[name]={ + name=name,path=path,source=false, + vol=0,volChanging=false, + pitch=1,pitchChanging=false, + lowgain=1,lowgainChanging=false, + highgain=1,highgainChanging=false, + } + end +end +local function _tryLoad(name) + if srcLib[name] then + local obj=srcLib[name] + if obj.source then + return true + elseif love.filesystem.getInfo(obj.path) then + obj.source=audio.newSource(obj.path,'stream') + obj.source:setLooping(true) + table.insert(lastLoadNames,1,name) + return true + else + LOG(STRING.repD("Wrong path for BGM '$1': $2",obj.name,obj.path),5) + end + elseif name then + LOG("No BGM: "..name,5) + end +end + +local BGM={} + +function BGM.getList() return nameList end +function BGM.getCount() return #nameList end + +function BGM.setDefault(bgms) + if type(bgms)=='string' then + bgms={bgms} + elseif type(bgms)=='table' then + for i=1,#bgms do assert(type(bgms[i])=='string',"BGM list must be list of strings") end + else + error("BGM.setDefault(bgms): bgms must be string or table") + end + defaultBGM=bgms +end +function BGM.setMaxSources(count) + assert(type(count)=='number' and count>0 and count%1==0,"BGM.setMaxSources(count): count must be positive integer") + maxLoadedCount=count + _updateSources() +end +function BGM.setVol(vol) + assert(type(vol)=='number' and vol>=0 and vol<=1,"BGM.setVol(vol): count must be in range 0~1") + volume=vol + for i=1,#nowPlay do + local bgm=nowPlay[i] + if not bgm.volChanging then + bgm.source:setVolume(bgm.vol*vol) + end + end +end +function BGM.init(name,path) + if type(name)=='table' then + for k,v in next,name do + _addFile(k,v) + end + else + _addFile(name,path) + end + table.sort(nameList) + LOG(BGM.getCount().." BGM files added") +end + +function BGM.play(bgms,args) + if not args then args='' end + if not bgms then bgms=defaultBGM end + if not bgms then return end + + if type(bgms)=='string' then bgms={bgms} end + assert(type(bgms)=='table',"BGM.play(name,args): name must be string or table") + + if + TABLE.compare(lastPlay,bgms) and + srcLib[lastPlay[1]] and srcLib[lastPlay[1]].source and + srcLib[lastPlay[1]].source:isPlaying() + then + return + end + + BGM.stop() + + if not STRING.sArg(args,'-preLoad') then + lastPlay=bgms + end + + for i=1,#bgms do + local bgm=bgms[i] + assert(type(bgm)=='string',"BGM list can only be list of string") + if _tryLoad(bgm) and not STRING.sArg(args,'-preLoad') then + local obj=srcLib[bgms[i]] + obj.vol=0 + obj.pitch=1 + obj.lowgain=1 + obj.highgain=1 + obj.volChanging=false + obj.pitchChanging=false + obj.lowgainChanging=false + obj.highgainChanging=false + + _clearTask(obj) + + local source=obj.source + source:setLooping(not STRING.sArg(args,'-noloop')) + source:setPitch(1) + source:seek(0) + source:setFilter() + if STRING.sArg(args,'-sdin') then + obj.vol=1 + source:setVolume(volume) + BGM.set(bgm,'volume',1,0) + else + source:setVolume(0) + BGM.set(bgm,'volume',1,.626) + end + source:play() + + table.insert(nowPlay,obj) + return true + end + end + _updateSources() +end +function BGM.stop(time) + if #nowPlay>0 then + for i=1,#nowPlay do + local obj=nowPlay[i] + _clearTask(obj,'volume') + if time==0 then + obj.source:stop() + obj.volChanging=false + else + TASK.new(task_setVolume,obj,0,time or .626,true) + obj.volChanging=true + end + end + TABLE.cut(nowPlay) + lastPlay=NONE + end +end + +---@param mode +---| 'volume' +---| 'lowgain' +---| 'highgain' +---| 'volume' +---| 'pitch' +---| 'seek' +function BGM.set(bgms,mode,...) + if type(bgms)=='string' then + if bgms=='all' then + bgms=nowPlay + else + bgms={srcLib[bgms]} + end + elseif type(bgms)=='table' then + bgms=TABLE.shift(bgms) + for i=1,#bgms do + assert(type(bgms[i])=='string',"BGM list must be list of strings") + bgms[i]=srcLib[bgms[i]] + end + else + error("BGM.play(name,args): name must be string or table") + end + for i=1,#bgms do + local obj=bgms[i] + if obj.source then + if mode=='volume' then + _clearTask(obj,'volume') + + local vol,time=... + if not time then time=1 end + + assert(type(vol)=='number' and vol>=0 and vol<=1,"BGM.set(...,volume): volume must be in range 0~1") + assert(type(time)=='number' and time>=0,"BGM.set(...,time): time must be positive number") + + TASK.new(task_setVolume,obj,vol,time) + elseif mode=='pitch' then + _clearTask(obj,'pitch') + + local pitch,changeTime=... + if not pitch then pitch=1 end + if not changeTime then changeTime=1 end + + assert(type(pitch)=='number' and pitch>0 and pitch<=32,"BGM.set(...,pitch): pitch must be in range 0~32") + assert(type(changeTime)=='number' and changeTime>=0,"BGM.set(...,time): time must be positive number") + + TASK.new(task_setPitch,obj,pitch,changeTime) + elseif mode=='seek' then + local time=... + assert(type(time)=='number',"BGM.set(...,time): time must be number") + obj.source:seek(MATH.clamp(time,0,obj.source:getDuration())) + elseif mode=='lowgain' then + if effectsSupported then + _clearTask(obj,'lowgain') + local lowgain,changeTime=... + if not lowgain then lowgain=1 end + if not changeTime then changeTime=1 end + + assert(type(lowgain)=='number' and lowgain>=0 and lowgain<=1,"BGM.set(...,lowgain,highgain): lowgain must be in range 0~1") + assert(type(changeTime)=='number' and changeTime>=0,"BGM.set(...,time): time must be positive number") + + TASK.new(task_setLowgain,obj,lowgain,changeTime) + obj.lowgain=lowgain + obj.source:setFilter{type='bandpass',lowgain=obj.lowgain,highgain=obj.highgain,volume=1} + end + elseif mode=='highgain' then + if effectsSupported then + _clearTask(obj,'highgain') + local highgain,changeTime=... + if not highgain then highgain=1 end + if not changeTime then changeTime=1 end + + assert(type(highgain)=='number' and highgain>=0 and highgain<=1,"BGM.set(...,lowgain,highgain): highgain must be in range 0~1") + assert(type(changeTime)=='number' and changeTime>=0,"BGM.set(...,time): time must be positive number") + + TASK.new(task_setHighgain,obj,highgain,changeTime) + obj.highgain=highgain + obj.source:setFilter{type='bandpass',lowgain=obj.lowgain,highgain=obj.highgain,volume=1} + end + else + error("BGM.set(...,mode): mode must be 'volume', 'pitch', or 'seek'") + end + end + end +end + +function BGM.getPlaying() + return TABLE.shift(lastPlay) +end +function BGM.isPlaying() + return #nowPlay>0 and nowPlay[1].source:isPlaying() +end +function BGM.tell() + if nowPlay[1] then + return nowPlay[1].source:tell() + end +end +function BGM.getDuration() + if nowPlay[1] then + return nowPlay[1].source:getDuration() + end +end + +return BGM diff --git a/Zframework/bitop.lua b/Zframework/bitop.lua new file mode 100644 index 000000000..183326772 --- /dev/null +++ b/Zframework/bitop.lua @@ -0,0 +1,366 @@ +local M = {_TYPE='module', _NAME='bitop.funcs', _VERSION='1.0-0'} + +local floor = math.floor + +local MOD = 2 ^ 32 +local MODM = MOD - 1 + +local function memoize(f) + + local mt = {} + local t = setmetatable({}, mt) + + function mt:__index(k) + local v = f(k) + t[k] = v + return v + end + + return t +end + +local function make_bitop_uncached(t, m) + local function bitop(a, b) + local res, p = 0, 1 + while a ~= 0 and b ~= 0 do + local am, bm = a % m, b % m + res = res + t[am][bm] * p + a = (a - am) / m + b = (b - bm) / m + p = p * m + end + res = res + (a + b) * p + return res + end + return bitop +end + +local function make_bitop(t) + local op1 = make_bitop_uncached(t, 2 ^ 1) + local op2 = memoize(function(a) + return memoize(function(b) + return op1(a, b) + end) + end) + return make_bitop_uncached(op2, 2 ^ (t.n or 1)) +end + +-- ok? probably not if running on a 32-bit int Lua number type platform +function M.tobit(x) + return x % 2 ^ 32 +end + +M.bxor = make_bitop{[0]={[0]=0, [1]=1}, [1]={[0]=1, [1]=0}, n=4} +local bxor = M.bxor + +function M.bnot(a) + return MODM - a +end +local bnot = M.bnot + +function M.band(a, b) + return ((a + b) - bxor(a, b)) / 2 +end +local band = M.band + +function M.bor(a, b) + return MODM - band(MODM - a, MODM - b) +end +local bor = M.bor + +local lshift, rshift -- forward declare + +function M.rshift(a, disp) -- Lua5.2 insipred + if disp < 0 then + return lshift(a, -disp) + end + return floor(a % 2 ^ 32 / 2 ^ disp) +end +rshift = M.rshift + +function M.lshift(a, disp) -- Lua5.2 inspired + if disp < 0 then + return rshift(a, -disp) + end + return (a * 2 ^ disp) % 2 ^ 32 +end +lshift = M.lshift + +function M.tohex(x, n) -- BitOp style + n = n or 8 + local up + if n <= 0 then + if n == 0 then + return '' + end + up = true + n = -n + end + x = band(x, 16 ^ n - 1) + return ('%0' .. n .. (up and 'X' or 'x')):format(x) +end +local tohex = M.tohex + +function M.extract(n, field, width) -- Lua5.2 inspired + width = width or 1 + return band(rshift(n, field), 2 ^ width - 1) +end +local extract = M.extract + +function M.replace(n, v, field, width) -- Lua5.2 inspired + width = width or 1 + local mask1 = 2 ^ width - 1 + v = band(v, mask1) -- required by spec? + local mask = bnot(lshift(mask1, field)) + return band(n, mask) + lshift(v, field) +end +local replace = M.replace + +function M.bswap(x) -- BitOp style + local a = band(x, 0xff); + x = rshift(x, 8) + local b = band(x, 0xff); + x = rshift(x, 8) + local c = band(x, 0xff); + x = rshift(x, 8) + local d = band(x, 0xff) + return lshift(lshift(lshift(a, 8) + b, 8) + c, 8) + d +end +local bswap = M.bswap + +function M.rrotate(x, disp) -- Lua5.2 inspired + disp = disp % 32 + local low = band(x, 2 ^ disp - 1) + return rshift(x, disp) + lshift(low, 32 - disp) +end +local rrotate = M.rrotate + +function M.lrotate(x, disp) -- Lua5.2 inspired + return rrotate(x, -disp) +end +local lrotate = M.lrotate + +M.rol = M.lrotate -- LuaOp inspired +M.ror = M.rrotate -- LuaOp insipred + +function M.arshift(x, disp) -- Lua5.2 inspired + local z = rshift(x, disp) + if x >= 0x80000000 then + z = z + lshift(2 ^ disp - 1, 32 - disp) + end + return z +end +local arshift = M.arshift + +function M.btest(x, y) -- Lua5.2 inspired + return band(x, y) ~= 0 +end + +-- +-- Start Lua 5.2 "bit32" compat section. +-- + +M.bit32 = {} -- Lua 5.2 'bit32' compatibility + +local function bit32_bnot(x) + return (-1 - x) % MOD +end +M.bit32.bnot = bit32_bnot + +local function bit32_bxor(a, b, c, ...) + local z + if b then + a = a % MOD + b = b % MOD + z = bxor(a, b) + if c then + z = bit32_bxor(z, c, ...) + end + return z + elseif a then + return a % MOD + else + return 0 + end +end +M.bit32.bxor = bit32_bxor + +local function bit32_band(a, b, c, ...) + local z + if b then + a = a % MOD + b = b % MOD + z = ((a + b) - bxor(a, b)) / 2 + if c then + z = bit32_band(z, c, ...) + end + return z + elseif a then + return a % MOD + else + return MODM + end +end +M.bit32.band = bit32_band + +local function bit32_bor(a, b, c, ...) + local z + if b then + a = a % MOD + b = b % MOD + z = MODM - band(MODM - a, MODM - b) + if c then + z = bit32_bor(z, c, ...) + end + return z + elseif a then + return a % MOD + else + return 0 + end +end +M.bit32.bor = bit32_bor + +function M.bit32.btest(...) + return bit32_band(...) ~= 0 +end + +function M.bit32.lrotate(x, disp) + return lrotate(x % MOD, disp) +end + +function M.bit32.rrotate(x, disp) + return rrotate(x % MOD, disp) +end + +function M.bit32.lshift(x, disp) + if disp > 31 or disp < -31 then + return 0 + end + return lshift(x % MOD, disp) +end + +function M.bit32.rshift(x, disp) + if disp > 31 or disp < -31 then + return 0 + end + return rshift(x % MOD, disp) +end + +function M.bit32.arshift(x, disp) + x = x % MOD + if disp >= 0 then + if disp > 31 then + return (x >= 0x80000000) and MODM or 0 + else + local z = rshift(x, disp) + if x >= 0x80000000 then + z = z + lshift(2 ^ disp - 1, 32 - disp) + end + return z + end + else + return lshift(x, -disp) + end +end + +function M.bit32.extract(x, field, ...) + local width = ... or 1 + if field < 0 or field > 31 or width < 0 or field + width > 32 then + error'out of range' + end + x = x % MOD + return extract(x, field, ...) +end + +function M.bit32.replace(x, v, field, ...) + local width = ... or 1 + if field < 0 or field > 31 or width < 0 or field + width > 32 then + error'out of range' + end + x = x % MOD + v = v % MOD + return replace(x, v, field, ...) +end + +-- +-- Start LuaBitOp "bit" compat section. +-- + +M.bit = {} -- LuaBitOp "bit" compatibility + +function M.bit.tobit(x) + x = x % MOD + if x >= 0x80000000 then + x = x - MOD + end + return x +end +local bit_tobit = M.bit.tobit + +function M.bit.tohex(x, ...) + return tohex(x % MOD, ...) +end + +function M.bit.bnot(x) + return bit_tobit(bnot(x % MOD)) +end + +local function bit_bor(a, b, c, ...) + if c then + return bit_bor(bit_bor(a, b), c, ...) + elseif b then + return bit_tobit(bor(a % MOD, b % MOD)) + else + return bit_tobit(a) + end +end +M.bit.bor = bit_bor + +local function bit_band(a, b, c, ...) + if c then + return bit_band(bit_band(a, b), c, ...) + elseif b then + return bit_tobit(band(a % MOD, b % MOD)) + else + return bit_tobit(a) + end +end +M.bit.band = bit_band + +local function bit_bxor(a, b, c, ...) + if c then + return bit_bxor(bit_bxor(a, b), c, ...) + elseif b then + return bit_tobit(bxor(a % MOD, b % MOD)) + else + return bit_tobit(a) + end +end +M.bit.bxor = bit_bxor + +function M.bit.lshift(x, n) + return bit_tobit(lshift(x % MOD, n % 32)) +end + +function M.bit.rshift(x, n) + return bit_tobit(rshift(x % MOD, n % 32)) +end + +function M.bit.arshift(x, n) + return bit_tobit(arshift(x % MOD, n % 32)) +end + +function M.bit.rol(x, n) + return bit_tobit(lrotate(x % MOD, n % 32)) +end + +function M.bit.ror(x, n) + return bit_tobit(rrotate(x % MOD, n % 32)) +end + +function M.bit.bswap(x) + return bit_tobit(bswap(x % MOD)) +end + +return M diff --git a/Zframework/clipboard.lua b/Zframework/clipboard.lua new file mode 100644 index 000000000..bfa41060d --- /dev/null +++ b/Zframework/clipboard.lua @@ -0,0 +1,103 @@ +local function _sanitize(content) + if type(content)=='boolean' then + content=content and 'true' or 'false' + end + if type(content)=='nil' then + content='' + end + if type(content)=='number' then + content=tostring(content) + end + if type(content)~='string' then + MES.new('error',"Invalid content type!") + MES.traceback() + return '' + end + return content +end + +if SYSTEM~='Web' then + local get=love.system.getClipboardText + local set=love.system.setClipboardText + return { + get=function() return get() or '' end, + set=function(content) set(_sanitize(content)) end, + setFreshInterval=NULL, + _update=NULL, + } +end + +if WEB_COMPAT_MODE then + local _clipboardBuffer='' + return { + get=function() + JS.newPromiseRequest( + JS.stringFunc( + [[ + window.navigator.clipboard + .readText() + .then((text) => _$_(text)) + .catch((e) => { + console.warn(e); + _$_(''); + }); + ]] + ), + function(data) _clipboardBuffer=data end, + function() _clipboardBuffer='' end, + 3, + 'getClipboardText' + ) + if TASK.lock('clipboard_compat_interval',2.6) then + _clipboardBuffer='' + MES.new('warn',"Web-Compat mode, paste again to confirm",2.6) + end + return _clipboardBuffer + end, + set=function(str) + JS.callJS(JS.stringFunc( + [[ + window.navigator.clipboard + .writeText('%s') + .then(() => console.log('Copied to clipboard')) + .catch((e) => console.warn(e)); + ]], + _sanitize(str) + )) + end, + setFreshInterval=NULL, + _update=NULL, + } +end + +local getCHN=love.thread.getChannel('CLIP_get') +local setCHN=love.thread.getChannel('CLIP_set') +local trigCHN=love.thread.getChannel('CLIP_trig') + +local clipboard_thread=love.thread.newThread('Zframework/clipboard_thread.lua') +local isStarted,errorMessage=clipboard_thread:start() + +if not isStarted then + MES.new("error",errorMessage,26) +end + +local freshInterval=1 +local timer=-.626 +return { + get=function() return getCHN:peek() or '' end, + set=function(content) setCHN:push(_sanitize(content)) end, + setFreshInterval=function(val) + freshInterval=val + end, + _update=function(dt) + timer=timer+dt + if timer>freshInterval then + if isStarted and not clipboard_thread:isRunning() then + MES.new("warn",clipboard_thread:getError(),26) + isStarted=false + end + trigCHN:push(timer) + timer=0 + end + end, +} diff --git a/Zframework/clipboard_thread.lua b/Zframework/clipboard_thread.lua new file mode 100644 index 000000000..8299c124b --- /dev/null +++ b/Zframework/clipboard_thread.lua @@ -0,0 +1,48 @@ +local getCHN=love.thread.getChannel('CLIP_get') +local setCHN=love.thread.getChannel('CLIP_set') +local trigCHN=love.thread.getChannel('CLIP_trig') + +JS=require'Zframework.js' +local sleep=require'love.timer'.sleep + +local retrieving=false +while true do + if trigCHN:getCount()>0 then + local dt=trigCHN:pop() + if setCHN:getCount()>0 then + while setCHN:getCount()>1 do setCHN:pop() end + -- Set Clipboard + JS.callJS(JS.stringFunc( + [[ + window.navigator.clipboard + .writeText('%s') + .then(() => console.log('Copied to clipboard')) + .catch((e) => console.warn(e)); + ]], + setCHN:pop() + )) + end + -- Get Clipboard + if not retrieving then + JS.newPromiseRequest( + JS.stringFunc[[ + window.navigator.clipboard + .readText() + .then((text) => _$_(text)) + .catch((e)=>{}); + ]], + function(data) + while getCHN:getCount()>0 do getCHN:pop() end + getCHN:push(data) + retrieving=false + end, + function() retrieving=false end, + 1, + 'getClipboardText' + ) + retrieving=true + end + JS.retrieveData(dt) + end + sleep(.001) +end diff --git a/Zframework/color.lua b/Zframework/color.lua new file mode 100644 index 000000000..b31259de4 --- /dev/null +++ b/Zframework/color.lua @@ -0,0 +1,235 @@ +local abs=math.abs + +-- Converts from HSV to RGB color. All arguments should be between 0 and 1, inclusively. +local function HSVToRGB(h,s,v,a) + if s<=0 then return v,v,v,a end + h=h%1*6 + local c=v*s + local x=abs((h-1)%2-1)*c + if h<1 then return v,x+v-c,v-c,a + elseif h<2 then return x+v-c,v,v-c,a + elseif h<3 then return v-c,v,x+v-c,a + elseif h<4 then return v-c,x+v-c,v,a + elseif h<5 then return x+v-c,v-c,v,a + else return v,v-c,x+v-c,a + end +end + +local function RGBToHSV(r,g,b,a) + local max=math.max(r,g,b) -- = value + local min=math.min(r,g,b) + local chroma=max-min + local hue=( + max==min and 0 or + max==r and (g-b)/chroma%6/6 or + max==g and (2+(b-r)/chroma)/6 or + (4+(r-g)/chroma)/6 + ) + local saturation=max==0 and 0 or chroma/max + return hue,saturation,max,a +end + +local COLOR={ + hsv=HSVToRGB, -- backwards compatibility + HSVToRGB=HSVToRGB, + RGBToHSV=RGBToHSV, + + + red= {HSVToRGB(0.00, 0.89, 0.91)}, + fire= {HSVToRGB(0.04, 0.93, 0.94)}, + orange= {HSVToRGB(0.09, 0.99, 0.96)}, + yellow= {HSVToRGB(0.15, 0.82, 0.90)}, + lime= {HSVToRGB(0.20, 0.89, 0.88)}, + jade= {HSVToRGB(0.25, 1.00, 0.82)}, + green= {HSVToRGB(0.33, 1.00, 0.81)}, + aqua= {HSVToRGB(0.47, 1.00, 0.76)}, + cyan= {HSVToRGB(0.53, 1.00, 0.88)}, + navy= {HSVToRGB(0.56, 1.00, 1.00)}, + sea= {HSVToRGB(0.61, 1.00, 1.00)}, + blue= {HSVToRGB(0.64, 1.00, 0.95)}, + violet= {HSVToRGB(0.74, 1.00, 0.91)}, + purple= {HSVToRGB(0.80, 1.00, 0.81)}, + magenta= {HSVToRGB(0.86, 1.00, 0.78)}, + wine= {HSVToRGB(0.92, 0.98, 0.91)}, + + lRed= {HSVToRGB(0.00, 0.38, 0.93)}, + lFire= {HSVToRGB(0.04, 0.45, 0.91)}, + lOrange= {HSVToRGB(0.10, 0.53, 0.92)}, + lYellow= {HSVToRGB(0.14, 0.61, 0.95)}, + lLime= {HSVToRGB(0.20, 0.66, 0.92)}, + lJade= {HSVToRGB(0.26, 0.56, 0.90)}, + lGreen= {HSVToRGB(0.34, 0.49, 0.89)}, + lAqua= {HSVToRGB(0.47, 0.59, 0.86)}, + lCyan= {HSVToRGB(0.51, 0.77, 0.88)}, + lNavy= {HSVToRGB(0.54, 0.80, 0.95)}, + lSea= {HSVToRGB(0.57, 0.72, 0.97)}, + lBlue= {HSVToRGB(0.64, 0.44, 0.96)}, + lViolet= {HSVToRGB(0.72, 0.47, 0.95)}, + lPurple= {HSVToRGB(0.80, 0.62, 0.89)}, + lMagenta= {HSVToRGB(0.86, 0.61, 0.89)}, + lWine= {HSVToRGB(0.93, 0.57, 0.92)}, + + dRed= {HSVToRGB(0.00, 0.80, 0.48)}, + dFire= {HSVToRGB(0.04, 0.80, 0.34)}, + dOrange= {HSVToRGB(0.07, 0.80, 0.39)}, + dYellow= {HSVToRGB(0.12, 0.80, 0.37)}, + dLime= {HSVToRGB(0.20, 0.80, 0.26)}, + dJade= {HSVToRGB(0.29, 0.80, 0.27)}, + dGreen= {HSVToRGB(0.33, 0.80, 0.26)}, + dAqua= {HSVToRGB(0.46, 0.80, 0.24)}, + dCyan= {HSVToRGB(0.50, 0.80, 0.30)}, + dNavy= {HSVToRGB(0.58, 0.80, 0.42)}, + dSea= {HSVToRGB(0.64, 0.80, 0.40)}, + dBlue= {HSVToRGB(0.67, 0.80, 0.34)}, + dViolet= {HSVToRGB(0.71, 0.80, 0.35)}, + dPurple= {HSVToRGB(0.76, 0.80, 0.32)}, + dMagenta= {HSVToRGB(0.87, 0.80, 0.28)}, + dWine= {HSVToRGB(0.92, 0.80, 0.28)}, + + black= {HSVToRGB(0.04, 0.04, 0.14)}, + dGray= {HSVToRGB(0.02, 0.05, 0.44)}, + gray= {HSVToRGB(0.02, 0.05, 0.65)}, + lGray= {HSVToRGB(0.02, 0.06, 0.86)}, + white= {HSVToRGB(0.01, 0.02, 0.99)}, + + xGray= {HSVToRGB(0.00, 0.00, 0.35,.8)}, + lxGray= {HSVToRGB(0.00, 0.00, 0.62,.8)}, + dxGray= {HSVToRGB(0.00, 0.00, 0.16,.8)}, +} +for k,v in next,{ + R='red', F='fire', O='orange', Y='yellow', L='lime', J='jade', G='green', A='aqua', C='cyan', N='navy', S='sea', B='blue', V='violet', P='purple', M='magenta', W='wine', + lR='lRed',lF='lFire',lO='lOrange',lY='lYellow',lL='lLime',lJ='lJade',lG='lGreen',lA='lAqua',lC='lCyan',lN='lNavy',lS='lSea',lB='lBlue',lV='lViolet',lP='lPurple',lM='lMagenta',lW='lWine', + dR='dRed',dF='dFire',dO='dOrange',dY='dYellow',dL='dLime',dJ='dJade',dG='dGreen',dA='dAqua',dC='dCyan',dN='dNavy',dS='dSea',dB='dBlue',dV='dViolet',dP='dPurple',dM='dMagenta',dW='dWine', + D='black',dH='dGray',H='gray',lH='lGray',Z='white', + X='xGray',lX='lxGray',dX='dxGray', + -- Remain letter: EIKQTU +} do + COLOR[k]=COLOR[v] +end +setmetatable(COLOR,{__index=function(_,k) + error("No color: "..tostring(k)) +end}) + + +do-- Random generators + local rnd=math.random + local list_norm={'red','fire','orange','yellow','lime','jade','green','aqua','cyan','navy','sea','blue','violet','purple','magenta','wine'} + local len_list_norm=#list_norm + function COLOR.random_norm() + return COLOR[list_norm[rnd(len_list_norm)]] + end + + local list_bright={'lRed','lFire','lOrange','lYellow','lLime','lJade','lGreen','lAqua','lCyan','lNavy','lSea','lBlue','lViolet','lPurple','lMagenta','lWine'} + local len_list_bright=#list_bright + function COLOR.random_bright() + return COLOR[list_bright[rnd(len_list_bright)]] + end + + local list_dark={'dRed','dFire','dOrange','dYellow','dLime','dJade','dGreen','dAqua','dCyan','dNavy','dSea','dBlue','dViolet','dPurple','dMagenta','dWine'} + local len_list_dark=#list_dark + function COLOR.random_dark() + return COLOR[list_dark[rnd(len_list_dark)]] + end +end + +do-- Rainbow generators + local twoThirdsOfPi=2*math.pi/3 + local sin=math.sin + function COLOR.rainbow(phase,alpha) + return + sin(phase)*.4+.6, + sin(phase+twoThirdsOfPi)*.4+.6, + sin(phase-twoThirdsOfPi)*.4+.6, + alpha + end + function COLOR.rainbow_light(phase,alpha) + return + sin(phase)*.2+.7, + sin(phase+twoThirdsOfPi)*.2+.7, + sin(phase-twoThirdsOfPi)*.2+.7, + alpha + end + function COLOR.rainbow_dark(phase,alpha) + return + sin(phase)*.2+.4, + sin(phase+twoThirdsOfPi)*.2+.4, + sin(phase-twoThirdsOfPi)*.2+.4, + alpha + end + function COLOR.rainbow_gray(phase,alpha) + return + sin(phase)*.16+.5, + sin(phase+twoThirdsOfPi)*.16+.5, + sin(phase-twoThirdsOfPi)*.16+.5, + alpha + end +end + +do -- Color interpolation https://www.alanzucconi.com/2016/01/06/colour-interpolation/ + local lerp=MATH.mix + function COLOR.interpolateRGB(rgba1,rgba2,t) + return + lerp(rgba1[1],rgba2[1],t), + lerp(rgba1[2],rgba2[2],t), + lerp(rgba1[3],rgba2[3],t), + ((rgba1[4] and rgba2[4]) and lerp(rgba1[4],rgba1[4],t) or nil) + end + + function COLOR.interpolateHSV(hsv1,hsv2,t) + local hue1,hue2=hsv1[1],hsv2[1] + if hue1>hue2 then + hue1,hue2=hue2,hue1 + t=1-t + end + + local hueDiff=hue2-hue1 + local finalHue=.0 + if hueDiff>.5 then + hue1=hue1+1 + finalHue=(hue1+t*(hue2-hue1))%1; + else + finalHue=hue1+t*hueDiff + end + + return finalHue, + lerp(hsv1[2],hsv2[2],t), + lerp(hsv1[3],hsv2[3],t), + ((hsv1[4] and hsv2[4]) and lerp(hsv1[4],hsv1[4],t) or nil) + end +end + +--[[ + Returns either color1 or color2 based on time. + Args: + - color1, color2: colors to switch between + - period: the length of time in a cycle in seconds. ("wavelength") + - percentage [optional]: percentage of time in the color1 phase (default: 50%) +]] +function COLOR.flicker(color1,color2,period,percentage) + percentage=percentage or .5 + return TIME()%period>percentage*period and color1 or color2 +end + +local lerpRGB,lerpHSV=COLOR.interpolateRGB,COLOR.interpolateHSV +local tau=MATH.tau +--[[ + Oscillates between color1 and color2 over time in a sinusoidal fashion. + Args: + - color1, color2: colors to switch between. + [FORMAT IS BASED ON TYPE, WHICH DEFAULTS TO HSV] + Use COLOR.HSVtoRGB() and/or COLOR.RGBtoHSV to convert between color formats. + - period: the length of time in a cycle in seconds. (wavelength) + - type [optional]: the type of interpolation, defaulting to 'hsv'. + Supported values: nil, 'hsv', 'rgb' +]] +function COLOR.sine(color1,color2,period,type) + type=type or 'hsv' + local t=math.sin(tau*TIME()/period) + if type=='hsv' then + return lerpHSV(color1, color2, t) + elseif type=='rgb' then + return lerpRGB(color1, color2, t) + end +end + +return COLOR diff --git a/Zframework/diacritics.txt b/Zframework/diacritics.txt new file mode 100644 index 000000000..3372094c1 --- /dev/null +++ b/Zframework/diacritics.txt @@ -0,0 +1,48 @@ +Á=A,Ă=A,Ắ=A,Ặ=A,Ằ=A,Ẳ=A,Ẵ=A,Ǎ=A,Â=A,Ấ=A,Ậ=A,Ầ=A,Ẩ=A,Ẫ=A,Ä=A,Ạ=A,À=A,Ả=A,Ā=A,Ą=A,Å=A,Ǻ=A,Ã=A,Æ=AE,Ǽ=AE +Ḅ=B,Ɓ=B,ʚ=B,ɞ=B +Ć=C,Č=C,Ç=C,Ĉ=C,Ċ=C,Ɔ=C,ʗ=C +Ď=D,Ḓ=D,Ḍ=D,Ɗ=D,Ḏ=D,Dz=DZ,Dž=DZ,Đ=D,Ð=D,DZ=DZ,DŽ=DZ +É=E,Ĕ=E,Ě=E,Ê=E,Ế=E,Ệ=E,Ề=E,Ể=E,Ễ=E,Ë=E,Ė=E,Ẹ=E,È=E,Ẻ=E,Ē=E,Ę=E,Ẽ=E,Ɛ=E,Ə=E +Ƒ=F +Ǵ=G,Ğ=G,Ǧ=G,Ģ=G,Ĝ=G,Ġ=G,Ḡ=G,ʛ=G +Ḫ=H,Ĥ=H,Ḥ=H,Ħ=H +Í=I,Ĭ=I,Ǐ=I,Î=I,Ï=I,İ=I,Ị=I,Ì=I,Ỉ=I,Ī=I,Į=I,Ĩ=I,IJ=IJ +Ĵ=J +Ķ=K,Ḳ=K,Ƙ=K,Ḵ=K +Ĺ=L,Ƚ=L,Ľ=L,Ļ=L,Ḽ=L,Ḷ=L,Ḹ=L,Ḻ=L,Ŀ=L,Lj=Lj,Ł=L,LJ=LJ +Ḿ=M,Ṁ=M,Ṃ=M +Ń=N,Ň=N,Ņ=N,Ṋ=N,Ṅ=N,Ṇ=N,Ǹ=N,Ɲ=N,Ṉ=N,Nj=Nj,Ñ=N,NJ=NJ +Ó=O,Ŏ=O,Ǒ=O,Ô=O,Ố=O,Ộ=O,Ồ=O,Ổ=O,Ỗ=O,Ö=O,Ọ=O,Ő=O,Ò=O,Ỏ=O,Ơ=O,Ớ=O,Ợ=O,Ờ=O,Ở=O,Ỡ=O,Ō=O,Ɵ=O,Ǫ=O,Ø=O,Ǿ=O,Õ=O,Œ=OE,ɶ=Oe +Þ=P +Ŕ=R,Ř=R,Ŗ=R,Ṙ=R,Ṛ=R,Ṝ=R,Ṟ=R,ʁ=R +Ś=S,Š=S,Ş=S,Ŝ=S,Ș=S,Ṡ=S,Ṣ=S,ẞ=S +Ť=T,Ţ=T,Ṱ=T,Ț=T,Ṭ=T,Ṯ=T,Ŧ=T,Þ=T,Ð=T +Ú=U,Ŭ=U,Ǔ=U,Û=U,Ü=U,Ǘ=U,Ǚ=U,Ǜ=U,Ǖ=U,Ụ=U,Ű=U,Ù=U,Ủ=U,Ư=U,Ứ=U,Ự=U,Ừ=U,Ử=U,Ữ=U,Ū=U,Ų=U,Ů=U,Ũ=U +Ẃ=W,Ŵ=W,Ẅ=W,Ẁ=W,ʬ=W +Ý=Y,Ŷ=Y,Ÿ=Y,Ẏ=Y,Ỵ=Y,Ỳ=Y,Ƴ=Y,Ỷ=Y,Ȳ=Y,Ỹ=Y +Ź=Z,Ž=Z,Ż=Z,Ẓ=Z,Ẕ=Z,Ƶ=Z +á=a,ă=a,ắ=a,ặ=a,ằ=a,ẳ=a,ẵ=a,ǎ=a,â=a,ấ=a,ậ=a,ầ=a,ẩ=a,ẫ=a,ä=a,ạ=a,à=a,ả=a,ā=a,ą=a,å=a,ǻ=a,ã=a,æ=a,ǽ=a,ɑ=a,ɐ=a,ɒ=a +ḅ=b,ɓ=b,ß=b +ć=c,č=c,ç=c,ĉ=c,ɕ=c,ċ=c +ď=d,ḓ=d,ḍ=d,ɗ=d,ḏ=d,đ=d,ɖ=d,ʤ=d,dz=d,ʣ=d,ʥ=d,dž=d,ð=d +é=e,ĕ=e,ě=e,ê=e,ế=e,ệ=e,ề=e,ể=e,ễ=e,ë=e,ė=e,ẹ=e,è=e,ẻ=e,ē=e,ę=e,ẽ=e,ʒ=e,ǯ=e,ʓ=e,ɘ=e,ɜ=e,ɝ=e,ə=e,ɚ=e,ʚ=e,ɞ=e +ƒ=f,ſ=f,ʩ=f,fi=f,fl=f,ʃ=f,ʆ=f,ʅ=f,ɟ=f,ʄ=f +ǵ=g,ğ=g,ǧ=g,ģ=g,ĝ=g,ġ=g,ɠ=g,ḡ=g,ɡ=g,ɣ=g +ḫ=h,ĥ=h,ḥ=h,ɦ=h,ẖ=h,ħ=h,ɧ=h,ɥ=h,ʮ=h,ʯ=h,ų=h +í=i,ĭ=i,ǐ=i,î=i,ï=i,ị=i,ì=i,ỉ=i,ī=i,į=i,ɨ=i,ĩ=i,ɩ=i,ı=i,ij=ij,ɟ=i +ǰ=j,ĵ=j,ʝ=j,ȷ=j,ɟ=j,ʄ=j +ķ=k,ḳ=k,ƙ=k,ḵ=k,ĸ=k,ʞ=k +ĺ=l,ƚ=l,ɬ=l,ľ=l,ļ=l,ḽ=l,ḷ=l,ḹ=l,ḻ=l,ŀ=l,ɫ=l,ɭ=l,ł=l,ƛ=l,ɮ=lz,lj=lj,ʪ=ls,ʫ=lz +ḿ=m,ṁ=m,ṃ=m,ɱ=m,ɯ=m,ɰ=m +ʼn=n,ń=n,ň=n,ņ=n,ṋ=n,ṅ=n,ṇ=n,ǹ=n,ɲ=n,ṉ=n,ɳ=n,ñ=n,nj=nj,ŋ=n,Ŋ=n +ó=o,ŏ=o,ǒ=o,ô=o,ố=o,ộ=o,ồ=o,ổ=o,ỗ=o,ö=o,ọ=o,ő=o,ò=o,ỏ=o,ơ=o,ớ=o,ợ=o,ờ=o,ở=o,ỡ=o,ō=o,ǫ=o,ø=o,ǿ=o,õ=o,ɛ=o,ɔ=o,ɵ=o,ʘ=o,œ=oe +ɸ=p,þ=p +ʠ=q +ŕ=r,ř=r,ŗ=r,ṙ=r,ṛ=r,ṝ=r,ɾ=r,ṟ=r,ɼ=r,ɽ=r,ɿ=r,ɹ=r,ɻ=r,ɺ=r +ś=s,š=s,ş=s,ŝ=s,ș=s,ṡ=s,ṣ=s,ʂ=s,ſ=s,ʃ=s,ʆ=s,ß=s,ʅ=s +ť=t,ţ=t,ṱ=t,ț=t,ẗ=t,ṭ=t,ṯ=t,ʈ=t,ŧ=t,ʨ=t,ʧ=t,þ=t,ð=t,ʦ=t,ʇ=t +ʉ=u,ú=u,ŭ=u,ǔ=u,û=u,ü=u,ǘ=u,ǚ=u,ǜ=u,ǖ=u,ụ=u,ű=u,ù=u,ủ=u,ư=u,ứ=u,ự=u,ừ=u,ử=u,ữ=u,ū=u,ų=u,ů=u,ũ=u,ʊ=u +ʋ=v,ʌ=v +ẃ=w,ŵ=w,ẅ=w,ẁ=w,ʍ=w +ý=y,ŷ=y,ÿ=y,ẏ=y,ỵ=y,ỳ=y,ƴ=y,ỷ=y,ȳ=y,ỹ=y,ʎ=y +ź=z,ž=z,ʑ=z,ż=z,ẓ=z,ẕ=z,ʐ=z,ƶ=z diff --git a/Zframework/file.lua b/Zframework/file.lua new file mode 100644 index 000000000..5f1088dc4 --- /dev/null +++ b/Zframework/file.lua @@ -0,0 +1,112 @@ +local fs=love.filesystem +local FILE={} +function FILE.isSafe(file) + return SYSTEM=='Web' or fs.getRealDirectory(file)~=fs.getSaveDirectory() +end +function FILE.load(name,args) + if not args then args='' end + if fs.getInfo(name) then + local F=fs.newFile(name) + assert(F:open'r','open error') + local s=F:read() F:close() + local mode= + STRING.sArg(args,'-luaon') and 'luaon' or + STRING.sArg(args,'-lua') and 'lua' or + STRING.sArg(args,'-json') and 'json' or + STRING.sArg(args,'-string') and 'string' or + s:sub(1,9):find('return%s*%{') and 'luaon' or + (s:sub(1,1)=='[' and s:sub(-1)==']' or s:sub(1,1)=='{' and s:sub(-1)=='}') and 'json' or + 'string' + if mode=='luaon' then + local func,err_mes=loadstring(s) + if func then + setfenv(func,{}) + local res=func() + return assert(res,'decode error') + else + error('decode error: '..err_mes) + end + elseif mode=='lua' then + local func,err_mes=loadstring(s) + if func then + local res=func() + return assert(res,'run error') + else + error('compile error: '..err_mes) + end + elseif mode=='json' then + local res=JSON.decode(s) + if res then + return res + end + error('decode error') + elseif mode=='string' then + return s + else + error('unknown mode') + end + elseif not STRING.sArg(args,'-canskip') then + error('no file') + end +end +function FILE.save(data,name,args) + if not args then args='' end + if STRING.sArg(args,'-d') and fs.getInfo(name) then + error('duplicate') + end + + if type(data)=='table' then + if STRING.sArg(args,'-luaon') then + if STRING.sArg(args,'-expand') then + data=TABLE.dump(data) + else + data='return'..TABLE.dumpDeflate(data) + end + if not data then + error('encode error') + end + else + data=JSON.encode(data) + if not data then + error('encode error') + end + end + else + data=tostring(data) + end + + local F=fs.newFile(name) + assert(F:open('w'),'open error') + F:write(data) F:flush() F:close() +end +function FILE.clear(path) + if not FILE.isSafe(path) and fs.getInfo(path).type=='directory' then + for _,name in next,fs.getDirectoryItems(path) do + name=path..'/'..name + if not FILE.isSafe(name) then + local t=fs.getInfo(name).type + if t=='file' then + fs.remove(name) + end + end + end + end +end +function FILE.clear_s(path) + if path=='' or (not FILE.isSafe(path) and fs.getInfo(path).type=='directory') then + for _,name in next,fs.getDirectoryItems(path) do + name=path..'/'..name + if not FILE.isSafe(name) then + local t=fs.getInfo(name).type + if t=='file' then + fs.remove(name) + elseif t=='directory' then + FILE.clear_s(name) + fs.remove(name) + end + end + end + fs.remove(path) + end +end +return FILE diff --git a/Zframework/font.lua b/Zframework/font.lua new file mode 100644 index 000000000..17f73e951 --- /dev/null +++ b/Zframework/font.lua @@ -0,0 +1,59 @@ +local set=GC.setFont +local fontFiles,fontCache={},{} +local defaultFont,defaultFallBack +local curFont=false-- Current using font object + +local FONT={} +function FONT.setDefault(name) defaultFont=name end +function FONT.setFallback(name) defaultFallBack=name end +function FONT.rawget(s) + if not fontCache[s] then + fontCache[s]=GC.setNewFont(s,'light',GC.getDPIScale()*SCR.k*2) + end + return fontCache[s] +end +function FONT.rawset(s) + set(fontCache[s] or FONT.rawget(s)) +end +function FONT.load(fonts) + for name,path in next,fonts do + assert(love.filesystem.getInfo(path),STRING.repD("Font file $1($2) not exist!",name,path)) + fontFiles[name]=love.filesystem.newFile(path) + fontCache[name]={} + end + FONT.reset() +end +function FONT.get(size,name) + if not name then name=defaultFont end + local f=fontCache[name][size] + if not f then + f=GC.setNewFont(fontFiles[name],size,'light',GC.getDPIScale()*SCR.k*2) + if defaultFallBack and name~=defaultFallBack then + f:setFallbacks(FONT.get(size,defaultFallBack)) + end + fontCache[name][size]=f + end + return f +end +function FONT.set(size,name) + if not name then name=defaultFont end + + local f=fontCache[name][size] + if f~=curFont then + curFont=f or FONT.get(size,name) + set(curFont) + end +end +function FONT.reset() + for name,cache in next,fontCache do + if type(cache)=='table' then + for size in next,cache do + cache[size]=FONT.get(size,name) + end + else + fontCache[name]=FONT.rawget(name) + end + end +end + +return FONT diff --git a/Zframework/gcExtend.lua b/Zframework/gcExtend.lua new file mode 100644 index 000000000..e2d6a965f --- /dev/null +++ b/Zframework/gcExtend.lua @@ -0,0 +1,184 @@ +local gc=love.graphics +local setColor,printf,draw=gc.setColor,gc.printf,gc.draw +local sin,cos=math.sin,math.cos + +local GC=setmetatable({},{ + __index=gc, + __metatable=true +}) +function GC.mStr(obj,x,y) printf(obj,x-626,y,1252,'center') end-- Printf a string with 'center' +function GC.simpX(obj,x,y) draw(obj,x-obj:getWidth()*.5,y) end-- Simply draw an obj with x=obj:getWidth()/2 +function GC.simpY(obj,x,y) draw(obj,x,y-obj:getHeight()*.5) end-- Simply draw an obj with y=obj:getWidth()/2 +function GC.X(obj,x,y,a,k) draw(obj,x,y,a,k,nil,obj:getWidth()*.5,0) end-- Draw an obj with x=obj:getWidth()/2 +function GC.Y(obj,x,y,a,k) draw(obj,x,y,a,k,nil,0,obj:getHeight()*.5) end-- Draw an obj with y=obj:getWidth()/2 +function GC.mDraw(obj,x,y,a,k) draw(obj,x,y,a,k,nil,obj:getWidth()*.5,obj:getHeight()*.5) end-- Draw an obj with both middle X & Y +function GC.outDraw(obj,div,x,y,a,k) + local w,h=obj:getWidth()*.5,obj:getHeight()*.5 + draw(obj,x-div,y-div,a,k,nil,w,h) + draw(obj,x-div,y+div,a,k,nil,w,h) + draw(obj,x+div,y-div,a,k,nil,w,h) + draw(obj,x+div,y+div,a,k,nil,w,h) +end +function GC.shadedPrint(str,x,y,mode,d,clr1,clr2) + local w=1280 + if mode=='center' then + x=x-w*.5 + elseif mode=='right' then + x=x-w + end + if not d then d=1 end + setColor(clr1 or COLOR.D) + printf(str,x-d,y-d,w,mode) + printf(str,x-d,y+d,w,mode) + printf(str,x+d,y-d,w,mode) + printf(str,x+d,y+d,w,mode) + setColor(clr2 or COLOR.Z) + printf(str,x,y,w,mode) +end +function GC.regPolygon(mode,x,y,R,segments,phase) + local l={} + local ang=phase or 0 + local angStep=6.283185307179586/segments + for i=1,segments do + l[2*i-1]=x+R*cos(ang) + l[2*i]=y+R*sin(ang) + ang=ang+angStep + end + gc.polygon(mode,l) +end +function GC.regRoundPolygon(mode,x,y,R,segments,r,phase) + local X,Y={},{} + local ang=phase or 0 + local angStep=6.283185307179586/segments + for i=1,segments do + X[i]=x+R*cos(ang) + Y[i]=y+R*sin(ang) + ang=ang+angStep + end + X[segments+1]=x+R*cos(ang) + Y[segments+1]=y+R*sin(ang) + local halfAng=6.283185307179586/segments/2 + local erasedLen=r*math.tan(halfAng) + if mode=='line' then + erasedLen=erasedLen+1-- Fix 1px cover + for i=1,segments do + -- Line + local x1,y1,x2,y2=X[i],Y[i],X[i+1],Y[i+1] + local dir=math.atan2(y2-y1,x2-x1) + gc.line(x1+erasedLen*cos(dir),y1+erasedLen*sin(dir),x2-erasedLen*cos(dir),y2-erasedLen*sin(dir)) + + -- Arc + ang=ang+angStep + local R2=R-r/cos(halfAng) + local arcCX,arcCY=x+R2*cos(ang),y+R2*sin(ang) + gc.arc('line','open',arcCX,arcCY,r,ang-halfAng,ang+halfAng) + end + elseif mode=='fill' then + local L={} + for i=1,segments do + -- Line + local x1,y1,x2,y2=X[i],Y[i],X[i+1],Y[i+1] + local dir=math.atan2(y2-y1,x2-x1) + L[4*i-3]=x1+erasedLen*cos(dir) + L[4*i-2]=y1+erasedLen*sin(dir) + L[4*i-1]=x2-erasedLen*cos(dir) + L[4*i]=y2-erasedLen*sin(dir) + + -- Arc + ang=ang+angStep + local R2=R-r/cos(halfAng) + local arcCX,arcCY=x+R2*cos(ang),y+R2*sin(ang) + gc.arc('fill','open',arcCX,arcCY,r,ang-halfAng,ang+halfAng) + end + gc.polygon('fill',L) + else + error("Draw mode should be 'line' or 'fill'") + end +end +do-- function GC.DO(L) + local cmds={ + origin="origin", + move="translate", + scale="scale", + rotate="rotate", + shear="shear", + clear="clear", + + setCL="setColor", + setCM="setColorMask", + setLW="setLineWidth", + setLS="setLineStyle", + setLJ="setLineJoin", + setBM="setBlendMode", + + print="print", + rawFT=function(...)FONT.rawset(...) end, + setFT=function(...)FONT.set(...) end, + mText=GC.mStr, + mDraw=GC.mDraw, + mDrawX=GC.X, + mDrawY=GC.Y, + mOutDraw=GC.outDraw, + + draw="draw", + line="line", + fRect=function(...)gc.rectangle('fill',...) end, + dRect=function(...)gc.rectangle('line',...) end, + fCirc=function(...)gc.circle('fill',...) end, + dCirc=function(...)gc.circle('line',...) end, + fElps=function(...)gc.ellipse('fill',...) end, + dElps=function(...)gc.ellipse('line',...) end, + fPoly=function(...)gc.polygon('fill',...) end, + dPoly=function(...)gc.polygon('line',...) end, + + dPie=function(...)gc.arc('line',...) end, + dArc=function(...)gc.arc('line','open',...) end, + dBow=function(...)gc.arc('line','closed',...) end, + fPie=function(...)gc.arc('fill',...) end, + fArc=function(...)gc.arc('fill','open',...) end, + fBow=function(...)gc.arc('fill','closed',...) end, + + fRPol=function(...)GC.regPolygon('fill',...) end, + dRPol=function(...)GC.regPolygon('line',...) end, + fRRPol=function(...)GC.regRoundPolygon('fill',...) end, + dRRPol=function(...)GC.regRoundPolygon('line',...) end, + } + local sizeLimit=gc.getSystemLimits().texturesize + function GC.DO(L) + gc.push() + local success,canvas + repeat + success,canvas=pcall(gc.newCanvas,math.min(L[1],sizeLimit),math.min(L[2],sizeLimit)) + if not success then + sizeLimit=math.floor(sizeLimit*.8) + end + until success + gc.setCanvas(canvas) + gc.origin() + gc.clear(1,1,1,0) + gc.setColor(1,1,1) + gc.setLineWidth(1) + for i=3,#L do + local cmd=L[i][1] + if type(cmd)=='boolean' and cmd then + table.remove(L[i],1) + cmd=L[i][1] + end + if type(cmd)=='string' then + local func=cmds[cmd] or gc[cmd] + if type(func)=='string' then + func=gc[func] + end + if func then + func(unpack(L[i],2)) + else + error("No gc command: "..cmd) + end + end + end + gc.setCanvas() + gc.pop() + return canvas + end +end +return GC diff --git a/Zframework/http.lua b/Zframework/http.lua new file mode 100644 index 000000000..4576c6613 --- /dev/null +++ b/Zframework/http.lua @@ -0,0 +1,195 @@ +local sendCHN=love.thread.getChannel('HTTP_inputChannel') +local recvCHN=love.thread.getChannel('HTTP_outputChannel') + +local threads={} +local threadCount=0 +local threadCode=[[ + local id=... + + local http=require'socket.http' + local ltn12=require'ltn12' + + local sendCHN=love.thread.getChannel('HTTP_inputChannel') + local recvCHN=love.thread.getChannel('HTTP_outputChannel') + local sleep=require'love.timer'.sleep + + while true do + -- local arg=sendCHN:demand() + -- Warning: workaround for love.js + while sendCHN:getCount()==0 do sleep(.0626) end + local arg=sendCHN:pop() + + if arg._destroy then + recvCHN:push{ + destroy=true, + id=id, + } + break + end + + -- print("\n------SEND------") for k,v in next,arg do print(k,v) end + local data={} + local _,code,detail=http.request{ + method=arg.method, + url=arg.url, + headers=arg.headers, + source=ltn12.source.string(arg.body), + + sink=ltn12.sink.table(data), + } + + local result={ + pool=arg.pool, + poolPtr=arg.poolPtr, + code=code, + body=table.concat(data), + detail=detail, + } + -- print("\n------RECV------") for k,v in next,result do print(k,v) end + recvCHN:push(result) + end +]] + +local msgPool=setmetatable({},{ + __index=function(self,k) + self[k]={} + return self[k] + end +}) + +local HTTP={ + _msgCount=0, + _trigTime=0, + _trigInterval=.26, + _host=false, +} + +local function addThread(num) + for i=1,26 do + if num<=0 then break end + if not threads[i] then + threads[i]=love.thread.newThread(threadCode) + threads[i]:start(i) + threadCount=threadCount+1 + num=num-1 + end + end +end + +function HTTP.request(arg) + arg.method=arg.method or arg.body and 'POST' or 'GET' + if arg.url then + assert(type(arg.url)=='string',"Field 'url' need string, get "..type(arg.url)) + if arg.url:sub(1,7)~='http://' then arg.url='http://'..arg.url end + else + arg.url=HTTP._host or error("Need url= or set default host with HTTP.setHost") + end + if arg.path then + assert(type(arg.path)=='string',"Field 'path' need string, get "..type(arg.path)) + arg.url=arg.url..arg.path + end + assert(arg.headers==nil or type(arg.headers)=='table',"Field 'headers' need table, get "..type(arg.headers)) + + if arg.body~=nil then + assert(type(arg.body)=='table',"Field 'body' need table, get "..type(arg.body)) + arg.body=JSON.encode(arg.body) + if not arg.headers then arg.headers={} end + TABLE.cover({ + ['Content-Type']="application/json", + ['Content-Length']=#arg.body, + },arg.headers) + end + + if arg.pool==nil then arg.pool='_default' end + arg.poolPtr=tostring(msgPool[arg.pool]) + + sendCHN:push(arg) +end + +function HTTP.reset() + for i=1,#threads do + threads[i]:release() + threads[i]=false + end + TABLE.clear(msgPool) + sendCHN:clear() + recvCHN:clear() + addThread(threadCount) +end +function HTTP.setThreadCount(n) + assert(type(n)=='number' and n>=1 and n<=26 and n%1==0,"function HTTP.setThreadCount(n): n must be integer from 1 to 26") + if n>threadCount then + addThread(n-threadCount) + else + for _=n+1,threadCount do + sendCHN:push{_destroy=true} + end + end +end +function HTTP.getThreadCount() + return threadCount +end +function HTTP.setInterval(interval) + if interval<=0 then interval=1e99 end + assert(type(interval)=='number',"Interval must be number") + HTTP._trigInterval=interval +end +function HTTP.clearPool(pool) + if pool==nil then pool='_default' end + assert(type(pool)=='string',"Pool must be nil or string") + HTTP._msgCount=HTTP._msgCount-#msgPool[pool] + msgPool[pool]={} +end +function HTTP.deletePool(pool) + assert(type(pool)=='string',"Pool must be nil or string") + assert(pool~='_default',"Cannot delete _default pool. What are you doing?") + HTTP._msgCount=HTTP._msgCount-#msgPool[pool] + msgPool[pool]=nil +end +function HTTP.pollMsg(pool) + if not (type(pool)=='nil' or type(pool)=='string') then error("Pool must be nil or string") end + HTTP.update() + local p=msgPool[pool or '_default'] + if #p>0 then + HTTP._msgCount=HTTP._msgCount-1 + return table.remove(p) + end +end +function HTTP.setHost(host) + assert(type(host)=='string',"Host must be string") + if host:sub(1,7)~='http://' then host='http://'..host end + HTTP._host=host +end + +function HTTP.update(dt) + if dt then + HTTP._trigTime=HTTP._trigTime+dt + if HTTP._trigTime>HTTP._trigInterval then + HTTP._trigTime=HTTP._trigTime%HTTP._trigInterval + else + return + end + end + while recvCHN:getCount()>0 do + local m=recvCHN:pop() + if m.destroy then + threads[m.id]:release() + threads[m.id]=false + elseif tostring(msgPool[m.pool])==m.poolPtr then -- If pool were cleared, discard this datapack + table.insert(msgPool[m.pool],{ + code=m.code, + body=m.body, + detail=m.detail, + }) + HTTP._msgCount=HTTP._msgCount+1 + end + end +end + +setmetatable(HTTP,{__call=function(self,arg) + return self.request(arg) +end,__metatable=true}) + +HTTP.reset() + +return HTTP diff --git a/Zframework/image.lua b/Zframework/image.lua new file mode 100644 index 000000000..149e6e5c0 --- /dev/null +++ b/Zframework/image.lua @@ -0,0 +1,25 @@ +local IMG={} +function IMG.init(list) + IMG.init=nil + + setmetatable(IMG,{__index=function(self,name) + if type(list[name])=='table' then + self[name]={} + for k,v in next,list[name] do + self[name][k]=love.graphics.newImage(v) + end + elseif type(list[name])=='string' then + self[name]=love.graphics.newImage(list[name]) + else + LOG("No IMG: "..name) + self[name]=PAPER + end + return self[name] + end}) + + function IMG.loadAll() + for k in next,list do local _=IMG[k] end + IMG.loadAll=nil + end +end +return IMG diff --git a/Zframework/init.lua b/Zframework/init.lua new file mode 100644 index 000000000..c59df4fb1 --- /dev/null +++ b/Zframework/init.lua @@ -0,0 +1,953 @@ +-- WARNING: This framework has been remade and renamed to Zenitha. Do not use this deprecated framework for your project + +NONE={}function NULL() end PAPER=love.graphics.newCanvas(1,1) +EDITING="" +LOADED=false + +---@type 'Windows'|'Android'|'Linux'|'iOS'|'macOS'|'Web' +SYSTEM=love.system.getOS() +WEB_COMPAT_MODE=false +if SYSTEM=='OS X' then + SYSTEM='macOS' +elseif SYSTEM=='Web' then + WEB_COMPAT_MODE=not love.thread.newThread('\n'):start() + print('Web compatible mode: ', WEB_COMPAT_MODE) +end + +-- Bit module +local success +success,bit=pcall(require,"bit") +if not success then + bit=require"Zframework.bitop".bit +end + +-- Pure lua modules (basic) +MATH= require'Zframework.mathExtend' +COLOR= require'Zframework.color' +TABLE= require'Zframework.tableExtend' +STRING= require'Zframework.stringExtend' +PROFILE= require'Zframework.profile' +JSON= require'Zframework.json' +TEST= require'Zframework.test' + +do-- Add pcall & MES for JSON lib + local encode,decode=JSON.encode,JSON.decode + JSON.encode=function(val) + local a,b=pcall(encode,val) + if a then + return b + elseif MES then + MES.traceback() + end + end + JSON.decode=function(str) + local a,b=pcall(decode,str) + if a then + return b + elseif MES then + MES.traceback() + end + end +end + +-- Pure lua modules (complex) +LOG= require'Zframework.log' +REQUIRE= require'Zframework.require' +TASK= require'Zframework.task' +LANG= require'Zframework.languages' +HASH= require'Zframework.sha2' +do + local bxor=bit.bxor + local char=string.char + local function sxor(s1, s2) + local b3="" + for i=1,#s1 do + b3=b3..char(bxor(s1:byte(i),s2:byte(i))) + end + return b3 + end + function HASH.pbkdf2(hashFunc, pw, salt, n) + local u=HASH.hex2bin(HASH.hmac(hashFunc, pw, salt.."\0\0\0\1")) + local t=u + + for _=2,n do + u=HASH.hex2bin(HASH.hmac(hashFunc, pw, u)) + t=sxor(t, u) + end + + return HASH.bin2hex(t):upper() + end +end + +-- Love-based modules (basic) +CLIPBOARD= require'Zframework.clipboard' +HTTP= require'Zframework.http' +WS= require'Zframework.websocket' +FILE= require'Zframework.file' +WHEELMOV= require'Zframework.wheelScroll' +SCR= require'Zframework.screen' +SCN= require'Zframework.scene' + +-- Love-based modules (complex) +GC= require'Zframework.gcExtend' +FONT= require'Zframework.font' +TEXT= require'Zframework.text' +SYSFX= require'Zframework.sysFX' +WAIT= require'Zframework.wait' +MES= require'Zframework.message' +BG= require'Zframework.background' +WIDGET= require'Zframework.widget' +VIB= require'Zframework.vibrate' +SFX= require'Zframework.sfx' +IMG= require'Zframework.image' +BGM= require'Zframework.bgm' +VOC= require'Zframework.voice' + +if SYSTEM=='Web' then + JS=require'Zframework.js' +end + +local ms,kb=love.mouse,love.keyboard +local KBisDown=kb.isDown + +local gc=love.graphics +local gc_push,gc_pop,gc_clear,gc_discard=gc.push,gc.pop,gc.clear,gc.discard +local gc_replaceTransform,gc_present=gc.replaceTransform,gc.present +local gc_setColor,gc_setLineWidth=gc.setColor,gc.setLineWidth +local gc_draw,gc_line,gc_circle,gc_print=gc.draw,gc.line,gc.circle,gc.print + +local BG,WIDGET,SCR,SCN,WAIT=BG,WIDGET,SCR,SCN,WAIT +local xOy=SCR.xOy +local ITP=xOy.inverseTransformPoint + +local max,min=math.max,math.min + +local debugMode +local mx,my,mouseShow,cursorSpd=640,360,false,0 +local jsState={}-- map, joystickID->axisStates: {axisName->axisVal} +local errData={}-- list, each error create {mes={errMes strings},scene=sceneNameStr} + +local function drawCursor(_,x,y) + gc_setColor(1,1,1) + gc_setLineWidth(2) + gc_circle(ms.isDown(1) and 'fill' or 'line',x,y,6) +end +local showPowerInfo=true +local showClickFX=true +local discardCanvas=false +local frameMul=100 +local sleepInterval=1/60 +local onQuit=NULL +local onBeforeQuit=false +local versionText="" + +local batteryImg=GC.DO{31,20, + {'fRect',1,0,26,2}, + {'fRect',1,18,26,2}, + {'fRect',0,1,2,18}, + {'fRect',26,1,2,18}, + {'fRect',29,3,2,14}, +} +local infoCanvas=gc.newCanvas(108,27) +local function updatePowerInfo() + local state,pow=love.system.getPowerInfo() + gc.setCanvas(infoCanvas) + gc_push('transform') + gc.origin() + gc_clear(0,0,0,.25) + if state~='unknown' then + gc_setLineWidth(4) + if state=='nobattery' then + gc_setColor(1,1,1) + gc_setLineWidth(2) + gc_line(74,5,100,22) + elseif pow then + if state=='charging' then gc_setColor(0,1,0) + elseif pow>50 then gc_setColor(1,1,1) + elseif pow>26 then gc_setColor(1,1,0) + elseif pow==26 then gc_setColor(.5,0,1) + else gc_setColor(1,0,0) + end + gc.rectangle('fill',76,6,pow*.22,14) + if pow<100 then + FONT.set(15) + gc.setColor(COLOR.D) + gc_print(pow,77,1) + gc_print(pow,77,3) + gc_print(pow,79,1) + gc_print(pow,79,3) + gc_setColor(COLOR.Z) + gc_print(pow,78,2) + end + end + gc_draw(batteryImg,73,3) + end + FONT.set(25) + gc_print(os.date("%H:%M"),3,-5) + gc_pop() + gc.setCanvas() +end + +------------------------------------------------------------- +local lastX,lastY=0,0-- Last click pos +local function _updateMousePos(x,y,dx,dy) + if SCN.swapping or WAIT.state then return end + dx,dy=dx/SCR.k,dy/SCR.k + if SCN.mouseMove then SCN.mouseMove(x,y,dx,dy) end + if ms.isDown(1) then + WIDGET.drag(x,y,dx,dy) + else + WIDGET.cursorMove(x,y) + end +end +local function mouse_update(dt) + if not KBisDown('lctrl','rctrl') and KBisDown('up','down','left','right') then + local dx,dy=0,0 + if KBisDown('up') then dy=dy-cursorSpd end + if KBisDown('down') then dy=dy+cursorSpd end + if KBisDown('left') then dx=dx-cursorSpd end + if KBisDown('right') then dx=dx+cursorSpd end + mx=max(min(mx+dx,1280),0) + my=max(min(my+dy,720),0) + if my==0 or my==720 then + WIDGET.sel=false + WIDGET.drag(0,0,0,-dy) + end + _updateMousePos(mx,my,dx,dy) + cursorSpd=min(cursorSpd+dt*26,12.6) + else + cursorSpd=6 + end +end +local function gp_update(js,dt) + local sx,sy=js._jsObj:getGamepadAxis('leftx'),js._jsObj:getGamepadAxis('lefty') + if math.abs(sx)>.1 or math.abs(sy)>.1 then + local dx,dy=0,0 + if sy<-.1 then dy=dy+2*sy*cursorSpd end + if sy>.1 then dy=dy+2*sy*cursorSpd end + if sx<-.1 then dx=dx+2*sx*cursorSpd end + if sx>.1 then dx=dx+2*sx*cursorSpd end + mx=max(min(mx+dx,1280),0) + my=max(min(my+dy,720),0) + if my==0 or my==720 then + WIDGET.sel=false + WIDGET.drag(0,0,0,-dy) + end + _updateMousePos(mx,my,dx,dy) + cursorSpd=min(cursorSpd+dt*26,12.6) + else + cursorSpd=6 + end +end +function love.mousepressed(x,y,k,touch) + if touch or WAIT.state then return end + mouseShow=true + mx,my=ITP(xOy,x,y) + if debugMode==1 then + print(("(%d,%d)<-%d,%d ~~(%d,%d)<-%d,%d"):format( + mx,my, + mx-lastX,my-lastY, + math.floor(mx/10)*10,math.floor(my/10)*10, + math.floor((mx-lastX)/10)*10,math.floor((my-lastY)/10)*10 + )) + end + if SCN.swapping then return end + if SCN.mouseDown then SCN.mouseDown(mx,my,k) end + WIDGET.press(mx,my,k) + lastX,lastY=mx,my + if showClickFX then SYSFX.newTap(3,mx,my) end +end +function love.mousemoved(x,y,dx,dy,touch) + if touch then return end + mouseShow=true + mx,my=ITP(xOy,x,y) + _updateMousePos(mx,my,dx,dy) +end +function love.mousereleased(x,y,k,touch) + if touch or WAIT.state or SCN.swapping then return end + mx,my=ITP(xOy,x,y) + if SCN.mouseUp then SCN.mouseUp(mx,my,k) end + if WIDGET.sel then + WIDGET.release(mx,my,k) + else + if lastX and SCN.mouseClick and (mx-lastX)^2+(my-lastY)^2<62 then + SCN.mouseClick(mx,my,k) + end + end +end +function love.wheelmoved(x,y) + if math.abs(x)>=100 then x=x/100 end + if math.abs(y)>=100 then y=y/100 end + if WAIT.state or SCN.swapping then return end + if SCN.wheelMoved then + SCN.wheelMoved(x,y) + else + WIDGET.unFocus() + WIDGET.drag(0,0,0,100*y) + end +end + +function love.touchpressed(id,x,y) + mouseShow=false + if WAIT.state or SCN.swapping then return end + if not SCN.mainTouchID then + SCN.mainTouchID=id + WIDGET.unFocus(true) + love.touchmoved(id,x,y,0,0) + end + x,y=ITP(xOy,x,y) + lastX,lastY=x,y + if SCN.touchDown then SCN.touchDown(x,y,id) end + if kb.hasTextInput() then kb.setTextInput(false) end + WIDGET.cursorMove(x,y) + WIDGET.press(x,y,1) +end +function love.touchmoved(id,x,y,dx,dy) + if WAIT.state or SCN.swapping then return end + x,y=ITP(xOy,x,y) + if SCN.touchMove then SCN.touchMove(x,y,dx/SCR.k,dy/SCR.k,id) end + WIDGET.drag(x,y,dx/SCR.k,dy/SCR.k) +end +function love.touchreleased(id,x,y) + if WAIT.state or SCN.swapping then return end + x,y=ITP(xOy,x,y) + if id==SCN.mainTouchID then + WIDGET.release(x,y,1) + WIDGET.cursorMove(x,y) + WIDGET.unFocus() + SCN.mainTouchID=false + end + if SCN.touchUp then SCN.touchUp(x,y,id) end + if (x-lastX)^2+(y-lastY)^2<62 then + if SCN.touchClick then SCN.touchClick(x,y) end + if showClickFX then SYSFX.newTap(3,x,y) end + end +end +-- function love.mousepressed(x,y,k) love.touchpressed(1,x,y) end +-- function love.mousemoved(x,y,dx,dy,touch) love.touchmoved(1,x,y,dx,dy) end +-- function love.mousereleased(x,y,k) love.touchreleased(1,x,y) end + +local globalKey={ + f8=function() + debugMode=1 + MES.new('info',"DEBUG ON",.2) + end +} +local fnKey={NULL,NULL,NULL,NULL,NULL,NULL,NULL} +local function debugKeyPressed(key) + if key=='f1' then fnKey[1]() + elseif key=='f2' then fnKey[2]() + elseif key=='f3' then fnKey[3]() + elseif key=='f4' then fnKey[4]() + elseif key=='f5' then fnKey[5]() + elseif key=='f6' then fnKey[6]() + elseif key=='f7' then fnKey[7]() + elseif key=='f8' then debugMode=nil MES.new('info',"DEBUG OFF",.2) + elseif key=='f9' then debugMode=1 MES.new('info',"DEBUG 1") + elseif key=='f10' then debugMode=2 MES.new('info',"DEBUG 2") + elseif key=='f11' then debugMode=3 MES.new('info',"DEBUG 3") + elseif key=='f12' then debugMode=4 MES.new('info',"DEBUG 4") + elseif debugMode==2 then + local W=WIDGET.sel + if W then + if key=='left' then W.x=W.x-10 + elseif key=='right' then W.x=W.x+10 + elseif key=='up' then W.y=W.y-10 + elseif key=='down' then W.y=W.y+10 + elseif key==',' then W.w=W.w-10 + elseif key=='.' then W.w=W.w+10 + elseif key=='/' then W.h=W.h-10 + elseif key=='\'' then W.h=W.h+10 + elseif key=='[' then W.font=W.font-5 + elseif key==']' then W.font=W.font+5 + else return + end + else + return + end + else + return + end + return true +end +function love.keypressed(key,_,isRep) + mouseShow=false + if debugMode and debugKeyPressed(key) then + -- Do nothing + elseif globalKey[key] then + globalKey[key]() + else + if SCN.swapping then return end + if WAIT.state then + if key=='escape' and WAIT.arg.escapable then WAIT.interrupt() end + return + end + if EDITING=="" and (not SCN.keyDown or SCN.keyDown(key,isRep)) then + local W=WIDGET.sel + if key=='escape' and not isRep then + SCN.back() + elseif key=='up' or key=='down' or key=='left' or key=='right' then + mouseShow=true + if KBisDown('lctrl','rctrl') then + if W and W.arrowKey then W:arrowKey(key) end + end + elseif key=='space' or key=='return' then + mouseShow=true + if not isRep then + if showClickFX then SYSFX.newTap(3,mx,my) end + love.mousepressed(mx,my,1) + love.mousereleased(mx,my,1) + end + else + if W and W.keypress then + W:keypress(key) + end + end + end + end +end +function love.keyreleased(i) + if WAIT.state or SCN.swapping then return end + if SCN.keyUp then SCN.keyUp(i) end +end + +function love.textedited(texts) + EDITING=texts +end +function love.textinput(texts) + WIDGET.textinput(texts) +end + +-- analog sticks: -1, 0, 1 for neg, neutral, pos +-- triggers: 0 for released, 1 for pressed +local jsAxisEventName={ + leftx={'leftstick_left','leftstick_right'}, + lefty={'leftstick_up','leftstick_down'}, + rightx={'rightstick_left','rightstick_right'}, + righty={'rightstick_up','rightstick_down'}, + triggerleft='triggerleft', + triggerright='triggerright' +} +local gamePadKeys={'a','b','x','y','back','guide','start','leftstick','rightstick','leftshoulder','rightshoulder','dpup','dpdown','dpleft','dpright'} +local dPadToKey={ + dpup='up', + dpdown='down', + dpleft='left', + dpright='right', + start='return', + back='escape', +} +function love.joystickadded(joystick) + table.insert(jsState,{ + _id=joystick:getID(), + _jsObj=joystick, + leftx=0,lefty=0, + rightx=0,righty=0, + triggerleft=0,triggerright=0 + }) + MES.new('info',"Joystick added") +end +function love.joystickremoved(joystick) + for i=1,#jsState do + if jsState[i]._jsObj==joystick then + for j=1,#gamePadKeys do + if joystick:isGamepadDown(gamePadKeys[j]) then + love.gamepadreleased(joystick,gamePadKeys[j]) + end + end + love.gamepadaxis(joystick,'leftx',0) + love.gamepadaxis(joystick,'lefty',0) + love.gamepadaxis(joystick,'rightx',0) + love.gamepadaxis(joystick,'righty',0) + love.gamepadaxis(joystick,'triggerleft',-1) + love.gamepadaxis(joystick,'triggerright',-1) + MES.new('info',"Joystick removed") + table.remove(jsState,i) + break + end + end +end +function love.gamepadaxis(joystick,axis,val) + if jsState[1] and joystick==jsState[1]._jsObj then + local js=jsState[1] + if axis=='leftx' or axis=='lefty' or axis=='rightx' or axis=='righty' then + local newVal=-- range: [0,1] + val>.4 and 1 or + val<-.4 and -1 or + 0 + if newVal~=js[axis] then + if js[axis]==-1 then + love.gamepadreleased(joystick,jsAxisEventName[axis][1]) + elseif js[axis]~=0 then + love.gamepadreleased(joystick,jsAxisEventName[axis][2]) + end + if newVal==-1 then + love.gamepadpressed(joystick,jsAxisEventName[axis][1]) + elseif newVal==1 then + love.gamepadpressed(joystick,jsAxisEventName[axis][2]) + end + js[axis]=newVal + end + elseif axis=='triggerleft' or axis=='triggerright' then + local newVal=val>.3 and 1 or 0-- range: [0,1] + if newVal~=js[axis] then + if newVal==1 then + love.gamepadpressed(joystick,jsAxisEventName[axis]) + else + love.gamepadreleased(joystick,jsAxisEventName[axis]) + end + js[axis]=newVal + end + end + end +end +function love.gamepadpressed(_,key) + mouseShow=false + if not SCN.swapping then + local cursorCtrl + if SCN.gamepadDown then + cursorCtrl=SCN.gamepadDown(key) + elseif SCN.keyDown then + cursorCtrl=SCN.keyDown(dPadToKey[key] or key) + else + cursorCtrl=true + end + if cursorCtrl then + key=dPadToKey[key] or key + mouseShow=true + local W=WIDGET.sel + if key=='back' then + SCN.back() + elseif key=='up' or key=='down' or key=='left' or key=='right' then + mouseShow=true + if W and W.arrowKey then W:arrowKey(key) end + elseif key=='return' then + mouseShow=true + if showClickFX then SYSFX.newTap(3,mx,my) end + love.mousepressed(mx,my,1) + love.mousereleased(mx,my,1) + else + if W and W.keypress then + W:keypress(key) + end + end + end + end +end +function love.gamepadreleased(_,i) + if WAIT.state or SCN.swapping then return end + if SCN.gamepadUp then SCN.gamepadUp(i) end +end + +function love.filedropped(file) + if WAIT.state or SCN.swapping then return end + if SCN.fileDropped then SCN.fileDropped(file) end +end +function love.directorydropped(dir) + if WAIT.state or SCN.swapping then return end + if SCN.directoryDropped then SCN.directoryDropped(dir) end +end +local autoGCcount=0 +function love.lowmemory() + collectgarbage() + if autoGCcount<3 then + autoGCcount=autoGCcount+1 + MES.new('check',"[auto GC] low MEM 设备内存过低") + end +end + +local onResize=NULL +function love.resize(w,h) + if SCR.w==w and SCR.h==h then return end + SCR.resize(w,h) + if BG.resize then BG.resize(w,h) end + if SCN.resize then SCN.resize(w,h) end + WIDGET.resize(w,h) + FONT.reset() + onResize(w,h) +end + +local onFocus=NULL +function love.focus(f) onFocus(f) end + +local yield=coroutine.yield +local function secondLoopThread() + local mainLoop=love.run() + repeat yield() until mainLoop() +end +function love.errorhandler(msg) + + if type(msg)~='string' then + msg="Unknown error" + elseif msg:find("Invalid UTF-8") and text then + msg=text.tryAnotherBuild + end + + -- Generate error message + local err={"Error:"..msg} + local c=2 + for l in debug.traceback("",2):gmatch("(.-)\n") do + if c>2 then + if not l:find("boot") then + err[c]=l:gsub("^\t*","") + c=c+1 + end + else + err[2]="Traceback" + c=3 + end + end + print(table.concat(err,"\n",1,c-2)) + + -- Reset something + love.audio.stop() + gc.reset() + + local sceneStack=SCN and table.concat(SCN.stack,"/") or "NULL" + if LOADED and #errData<3 then + BG.set('none') + table.insert(errData,{mes=err,scene=sceneStack}) + + -- Write messages to log file + love.filesystem.append('conf/error.log', + os.date("%Y/%m/%d %A %H:%M:%S\n").. + #errData.." crash(es) "..SYSTEM.."-"..VERSION.string.." scene: "..sceneStack.."\n".. + table.concat(err,"\n",1,c-2).."\n\n" + ) + + -- Get screencapture + gc.captureScreenshot(function(_) errData[#errData].shot=gc.newImage(_) end) + gc.present() + + -- Create a new mainLoop thread to keep game alive + local status,resume=coroutine.status,coroutine.resume + local loopThread=coroutine.create(secondLoopThread) + local res,threadErr + repeat + res,threadErr=resume(loopThread) + until status(loopThread)=='dead' + if not res then + love.errorhandler(threadErr) + return + end + else + ms.setVisible(true) + + local errorMsg + errorMsg=LOADED and + "Too many errors or fatal error occured.\nPlease restart the game." or + "An error has occurred during loading.\nError info has been created, and you can send it to the author." + while true do + love.event.pump() + for E,a,b in love.event.poll() do + if E=='quit' or a=='escape' then + return true + elseif E=='resize' then + SCR.resize(a,b) + end + end + gc_clear(.3,.5,.9) + gc_push('transform') + gc_replaceTransform(SCR.xOy) + FONT.set(100)gc_print(":(",100,0,0,1.2) + FONT.set(40)gc.printf(errorMsg,100,160,SCR.w0-100) + FONT.set(20) + gc_print(SYSTEM.."-"..VERSION.string.." scene:"..sceneStack,100,660) + gc.printf(err[1],100,360,1260-100) + gc_print("TRACEBACK",100,450) + for i=4,#err-2 do + gc_print(err[i],100,400+20*i) + end + gc_pop() + gc_present() + love.timer.sleep(.26) + end + end +end +love.threaderror=nil + +love.draw,love.update=nil-- remove default draw/update + +local debugColor={ + COLOR.Z, + COLOR.lM, + COLOR.lG, + COLOR.lB, +} + +local debugInfos={ + {"Cache",gcinfo}, +} +function love.run() + local love=love + + local TEXT_update,TEXT_draw=TEXT.update,TEXT.draw + local MES_update,MES_draw=MES.update,MES.draw + local HTTP_update,WS_update=HTTP.update,WS.update + local TASK_update=TASK.update + local SYSFX_update,SYSFX_draw=SYSFX.update,SYSFX.draw + local WIDGET_update,WIDGET_draw=WIDGET.update,WIDGET.draw + local STEP,SLEEP=love.timer.step,love.timer.sleep + local FPS,MINI=love.timer.getFPS,love.window.isMinimized + local PUMP,POLL=love.event.pump,love.event.poll + + local timer=love.timer.getTime + + local frameTimeList={} + local lastFrame=timer() + local lastFreshPow=lastFrame + local FCT=0-- Framedraw counter, from 0~99 + + love.resize(gc.getWidth(),gc.getHeight()) + + -- Scene Launch + while #SCN.stack>0 do SCN.pop() end + if #errData>0 then + SCN.cur='error' + SCN.init('error') + else + SCN.init('load') + end + + return function() + local _ + + local time=timer() + local dt=time-lastFrame + lastFrame=time + + -- EVENT + PUMP() + for N,a,b,c,d,e in POLL() do + if love[N] then + love[N](a,b,c,d,e) + elseif N=='quit' then + if onBeforeQuit then + onBeforeQuit() + onBeforeQuit=false + else + onQuit() + return a or true + end + end + end + + -- UPDATE + STEP() + if SYSTEM == 'Web' then + JS.retrieveData(dt) + CLIPBOARD._update(dt) + end + if mouseShow then mouse_update(dt) end + if next(jsState) then gp_update(jsState[1],dt) end + VOC.update() + BG.update(dt) + TEXT_update(dt) + WAIT.update(dt) + MES_update(dt) + HTTP_update(dt) + WS_update(dt) + TASK_update(dt) + SYSFX_update(dt) + if SCN.update then SCN.update(dt) end + if SCN.swapping then SCN.swapUpdate(dt) end + WIDGET_update(dt) + + -- DRAW + if not MINI() then + FCT=FCT+frameMul + if FCT>=100 then + FCT=FCT-100 + + gc_replaceTransform(SCR.origin) + gc_setColor(1,1,1) + BG.draw() + gc_replaceTransform(SCR.xOy) + if SCN.draw then SCN.draw() end + WIDGET_draw() + gc_replaceTransform(SCR.xOy) + SYSFX_draw() + TEXT_draw() + + -- Draw cursor + if mouseShow then drawCursor(time,mx,my) end + gc_replaceTransform(SCR.xOy_ul) + if showPowerInfo then + gc.translate(0,27) + end + MES_draw() + gc_replaceTransform(SCR.origin) + -- Draw power info. + if showPowerInfo then + gc_setColor(1,1,1) + gc_draw(infoCanvas,SCR.safeX,0,0,SCR.k) + end + + -- Draw scene swapping animation + if SCN.swapping then + gc_setColor(1,1,1) + _=SCN.state + _.draw(_.time) + end + gc_replaceTransform(SCR.xOy_d) + -- Draw Version string + gc_setColor(.9,.9,.9,.42) + FONT.set(20) + GC.mStr(versionText,0,-30) + gc_replaceTransform(SCR.xOy_dl) + local safeX=SCR.safeX/SCR.k + + -- Draw FPS + FONT.set(15) + gc_setColor(1,1,1) + gc_print(FPS(),safeX+5,-20) + + -- Debug info. + if debugMode then + -- Debug infos at left-down + gc_setColor(debugColor[debugMode]) + + -- Text infos + for i=1,#debugInfos do + gc_print(debugInfos[i][1],safeX+5,-20-20*i) + gc_print(debugInfos[i][2](),safeX+62.6,-20-20*i) + end + + -- Update & draw frame time + table.insert(frameTimeList,1,dt)table.remove(frameTimeList,126) + gc_setColor(1,1,1,.3) + for i=1,#frameTimeList do + gc.rectangle('fill',150+2*i,-20,2,-frameTimeList[i]*4000) + end + + -- Cursor pos disp + gc_replaceTransform(SCR.origin) + local x,y=SCR.xOy:transformPoint(mx,my) + gc_setLineWidth(1) + gc_line(x,0,x,SCR.h) + gc_line(0,y,SCR.w,y) + local t=math.floor(mx+.5)..","..math.floor(my+.5) + gc.setColor(COLOR.D) + gc_print(t,x+1,y) + gc_print(t,x+1,y-1) + gc_print(t,x+2,y-1) + gc_setColor(COLOR.Z) + gc_print(t,x+2,y) + + gc_replaceTransform(SCR.xOy_dr) + -- Websocket status + local status=WS.status('game') + if status=='dead' then + gc_setColor(COLOR.R) + elseif status=='connecting' then + gc_setColor(1,1,1,.5+.3*math.sin(time*6.26)) + elseif status=='running' then + gc_setColor(COLOR.lG) + end + gc.rectangle('fill',-16,-16,12,12) + local t1,t2,t3=WS.getTimers('game') + if t1>0 then gc_setColor(.9,.9,.9,t1)gc.rectangle('fill',-60,-2,-16,-16) end + if t2>0 then gc_setColor(.3,1,.3,t2)gc.rectangle('fill',-42,-2,-16,-16) end + if t3>0 then gc_setColor(1,.2,.2,t3)gc.rectangle('fill',-24,-2,-16,-16) end + end + gc_replaceTransform(SCR.origin) + WAIT.draw() + gc_present() + + -- SPEED UPUPUP! + if discardCanvas then gc_discard() end + end + end + + -- Fresh power info. + if time-lastFreshPow>2.6 then + if showPowerInfo then + updatePowerInfo() + lastFreshPow=time + end + if gc.getWidth()~=SCR.w or gc.getHeight()~=SCR.h then + love.resize(gc.getWidth(),gc.getHeight()) + end + end + + -- Slow debugmode + if debugMode then + if debugMode==3 then + SLEEP(.1) + elseif debugMode==4 then + SLEEP(.5) + end + end + + _=timer()-lastFrame + if _0) then + str=str:format(unpack(arg)) + end + str=str:gsub("[\n\t]","") + return str +end + +--The call will store in the webDB the return value from the function passed it timeouts +local function retrieveJS(funcToCall,filename) + --Used for retrieveData function + JS.callJS(("FS.writeFile('%s/%s',%s);"):format(love.filesystem.getSaveDirectory(),filename,funcToCall)) +end + +--Call JS.newRequest instead +function _Request:new(isPromise,command,onDataLoaded,onError,timeout,id) + local obj={} + setmetatable(obj,self) + obj.command=command + obj.onError=onError or __defaultErrorFunction + if not isPromise then + retrieveJS(command,self.filename) + else + JS.callJS(command) + end + obj.onDataLoaded=onDataLoaded + obj.timeOut=(timeout==nil) and obj.timeOut or timeout + obj.id=id + obj.filename="__temp"..id + + + function obj:getData() + --Try to read from webdb + if love.filesystem.getInfo(self.filename) then + return love.filesystem.read(self.filename) + end + end + + function obj:purgeData() + --Data must be purged for not allowing old data to be retrieved + love.filesystem.remove(self.filename) + end + + function obj:update(dt) + self.timeOut=self.timeOut-dt + local retData=self:getData() + + if ((retData~=nil and retData~="nil") or self.timeOut<=0) then + if (retData~=nil and retData:match("ERROR")==nil) then + if isDebugActive then + print("Data has been retrieved "..retData) + end + self.onDataLoaded(retData) + else + self.onError(self.id,retData) + end + self:purgeData() + return false + else + return true + end + end + return obj +end + +--Place this function on love.update and set it to return if it returns false (This API is synchronous) +function JS.retrieveData(dt) + local isRetrieving=#__requestQueue~=0 + local deadRequests={} + for i=1,#__requestQueue do + local isUpdating=__requestQueue[i]:update(dt) + if not isUpdating then + table.insert(deadRequests,i) + end + end + for i=1,#deadRequests do + if (isDebugActive) then + print("Request died: "..deadRequests[i]) + end + table.remove(__requestQueue,deadRequests[i]) + end + return isRetrieving +end + +--May only be used for functions that don't return a promise +function JS.newRequest(funcToCall,onDataLoaded,onError,timeout,optionalId) + table.insert(__requestQueue,_Request:new(false,funcToCall,onDataLoaded,onError,timeout or 5,optionalId or _requestCount)) +end + +--This function can be handled manually (in JS code) +--How to: add the function call when your events resolve: FS.writeFile("Put love.filesystem.getSaveDirectory here", "Pass a string here (NUMBER DONT WORK")) +--Or it can be handled by Lua, it auto sets your data if you write the following command: +-- _$_(yourStringOrFunctionHere) +function JS.newPromiseRequest(funcToCall,onDataLoaded,onError,timeout,optionalId) + optionalId=optionalId or _requestCount + funcToCall=funcToCall:gsub("_$_%(","FS.writeFile('"..love.filesystem.getSaveDirectory().."/__temp"..optionalId.."', ") + table.insert(__requestQueue,_Request:new(true,funcToCall,onDataLoaded,onError,timeout or 5,optionalId)) +end + + +--It receives the ID from ther request +--Don't try printing the request.command, as it will execute the javascript command +function JS.setDefaultErrorFunction(func) + __defaultErrorFunction=func +end + +JS.setDefaultErrorFunction(function(id,error) + if (isDebugActive) then + local msg="Data could not be loaded for id:'"..id.."'" + if (error) then + msg=msg.."\nError: "..error + end + print(msg) + end +end) + + +JS.callJS(JS.stringFunc("__getWebDB('%s');","__LuaJSDB")) + +return JS diff --git a/Zframework/json.lua b/Zframework/json.lua new file mode 100644 index 000000000..75541ea4c --- /dev/null +++ b/Zframework/json.lua @@ -0,0 +1,340 @@ +-- json.lua + +-- Copyright (c) 2020 rxi + +-- 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. + +-- Editted by MrZ + +local ins,char=table.insert,string.char +local json = {} + +------------------------------------------------------------------------------- +-- Encode +------------------------------------------------------------------------------- + +local _encode + +local escape_char_map = { + ["\\"] = "\\", + ["\""] = "\"", + ["\b"] = "b", + ["\f"] = "f", + ["\n"] = "n", + ["\r"] = "r", + ["\t"] = "t" +} + +local escape_char_map_inv = {["/"] = "/"} +for k, v in pairs(escape_char_map) do escape_char_map_inv[v] = k end + +local function escape_char(c) + return "\\" .. (escape_char_map[c] or string.format("u%04x", c:byte())) +end + +local function encode_nil() return "null" end + +local function encode_table(val, stack) + local res = {} + stack = stack or {} + + -- Circular reference? + if stack[val] then error("circular reference") end + + stack[val] = true + + if rawget(val, 1) ~= nil or next(val) == nil then + -- Treat as array -- check keys are valid and it is not sparse + local n = 0 + for k in pairs(val) do + if type(k) ~= 'number' then + error("invalid table: mixed or invalid key types") + end + n = n + 1 + end + if n ~= #val then error("invalid table: sparse array") end + -- Encode + for _, v in ipairs(val) do ins(res, _encode(v, stack)) end + stack[val] = nil + return "[" .. table.concat(res, ",") .. "]" + + else + -- Treat as an object + for k, v in pairs(val) do + if type(k) ~= 'string' then + error("invalid table: mixed or invalid key types") + end + ins(res, _encode(k, stack) .. ":" .. _encode(v, stack)) + end + stack[val] = nil + return "{" .. table.concat(res, ",") .. "}" + end +end + +local function encode_string(val) + return '"' .. val:gsub('[%z\1-\31\\"]', escape_char) .. '"' +end + +local function encode_number(val) + -- Check for NaN, -inf and inf + if val ~= val or val <= -math.huge or val >= math.huge then + error("unexpected number value '" .. tostring(val) .. "'") + end + return string.format("%.14g", val) +end + +local type_func_map = { + ['nil'] = encode_nil, + ['table'] = encode_table, + ['string'] = encode_string, + ['number'] = encode_number, + ['boolean'] = tostring +} + +_encode = function(val, stack) + local t = type(val) + local f = type_func_map[t] + if f then return f(val, stack) end + error("unexpected type '" .. t .. "'") +end + +json.encode=_encode + +------------------------------------------------------------------------------- +-- Decode +------------------------------------------------------------------------------- + +local parse + +local function create_set(...) + local res = {} + for i = 1, select("#", ...) do res[select(i, ...)] = true end + return res +end + +local space_chars = create_set(" ", "\t", "\r", "\n") +local delim_chars = create_set(" ", "\t", "\r", "\n", "]", "}", ",") +local escape_chars = create_set("\\", "/", '"', "b", "f", "n", "r", "t", "u") +local literals = create_set("true", "false", "null") + +local literal_map = {["true"] = true, ["false"] = false, ["null"] = nil} + +local function next_char(str, idx, set, negate) + for i = idx, #str do if set[str:sub(i, i)] ~= negate then return i end end + return #str + 1 +end + +local function decode_error(str, idx, msg) + local line_count = 1 + local col_count = 1 + for i = 1, idx - 1 do + col_count = col_count + 1 + if str:sub(i, i) == "\n" then + line_count = line_count + 1 + col_count = 1 + end + end + error(string.format("%s at line %d col %d", msg, line_count, col_count)) +end + +local function codepoint_to_utf8(n) + -- http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=iws-appendixa + local f = bit.rshift + if n <= 0x7f then + return char(n) + elseif n <= 0x7ff then + return char(f(n, 6) + 192, n % 64 + 128) + elseif n <= 0xffff then + return char(f(n, 12) + 224, f(n % 4096, 6) + 128, n % 64 + 128) + elseif n <= 0x10ffff then + return char(f(n, 18) + 240, f(n % 262144, 12) + 128, f(n % 4096, 6) + 128, n % 64 + 128) + end + error(string.format("invalid unicode codepoint '%x'", n)) +end + +local function parse_unicode_escape(s) + local n1 = tonumber(s:sub(1, 4), 16) + local n2 = tonumber(s:sub(7, 10), 16) + -- Surrogate pair? + if n2 then + return + codepoint_to_utf8((n1 - 0xd800) * 0x400 + (n2 - 0xdc00) + 0x10000) + else + return codepoint_to_utf8(n1) + end +end + +local function parse_string(str, i) + local res = "" + local j = i + 1 + local k = j + + while j <= #str do + local x = str:byte(j) + + if x < 32 then + decode_error(str, j, "control character in string") + + elseif x == 92 then -- `\`: Escape + res = res .. str:sub(k, j - 1) + j = j + 1 + local c = str:sub(j, j) + if c == "u" then + local hex = str:match("^[dD][89aAbB]%x%x\\u%x%x%x%x", j + 1) or + str:match("^%x%x%x%x", j + 1) or + decode_error(str, j - 1, + "invalid unicode escape in string") + res = res .. parse_unicode_escape(hex) + j = j + #hex + else + if not escape_chars[c] then + decode_error(str, j - 1, + "invalid escape char '" .. c .. "' in string") + end + res = res .. escape_char_map_inv[c] + end + k = j + 1 + + elseif x == 34 then -- `"`: End of string + res = res .. str:sub(k, j - 1) + return res, j + 1 + end + + j = j + 1 + end + + decode_error(str, i, "expected closing quote for string") +end + +local function parse_number(str, i) + local x = next_char(str, i, delim_chars) + local s = str:sub(i, x - 1) + local n = tonumber(s) + if not n then decode_error(str, i, "invalid number '" .. s .. "'") end + return n, x +end + +local function parse_literal(str, i) + local x = next_char(str, i, delim_chars) + local word = str:sub(i, x - 1) + if not literals[word] then + decode_error(str, i, "invalid literal '" .. word .. "'") + end + return literal_map[word], x +end + +local function parse_array(str, i) + local res = {} + local n = 1 + i = i + 1 + while 1 do + local x + i = next_char(str, i, space_chars, true) + -- Empty / end of array? + if str:sub(i, i) == "]" then + i = i + 1 + break + end + -- Read token + x, i = parse(str, i) + res[n] = x + n = n + 1 + -- Next token + i = next_char(str, i, space_chars, true) + local chr = str:sub(i, i) + i = i + 1 + if chr == "]" then break end + if chr ~= "," then decode_error(str, i, "expected ']' or ','") end + end + return res, i +end + +local function parse_object(str, i) + local res = {} + i = i + 1 + while 1 do + local key, val + i = next_char(str, i, space_chars, true) + -- Empty / end of object? + if str:sub(i, i) == "}" then + i = i + 1 + break + end + -- Read key + if str:sub(i, i) ~= '"' then + decode_error(str, i, "expected string for key") + end + key, i = parse(str, i) + -- Read ':' delimiter + i = next_char(str, i, space_chars, true) + if str:sub(i, i) ~= ":" then + decode_error(str, i, "expected ':' after key") + end + i = next_char(str, i + 1, space_chars, true) + -- Read value + val, i = parse(str, i) + -- Set + res[key] = val + -- Next token + i = next_char(str, i, space_chars, true) + local chr = str:sub(i, i) + i = i + 1 + if chr == "}" then break end + if chr ~= "," then decode_error(str, i, "expected '}' or ','") end + end + return res, i +end + +local char_func_map = { + ['"'] = parse_string, + ["0"] = parse_number, + ["1"] = parse_number, + ["2"] = parse_number, + ["3"] = parse_number, + ["4"] = parse_number, + ["5"] = parse_number, + ["6"] = parse_number, + ["7"] = parse_number, + ["8"] = parse_number, + ["9"] = parse_number, + ["-"] = parse_number, + ["t"] = parse_literal, + ["f"] = parse_literal, + ["n"] = parse_literal, + ["["] = parse_array, + ["{"] = parse_object +} + +function parse(str, idx) + local chr = str:sub(idx, idx) + local f = char_func_map[chr] + if f then return f(str, idx) end + decode_error(str, idx, "unexpected character '" .. chr .. "'") +end + +function json.decode(str) + if type(str) ~= 'string' then + error("expected argument of type string, got " .. type(str)) + end + local res, idx = parse(str, next_char(str, 1, space_chars, true)) + idx = next_char(str, idx, space_chars, true) + if idx <= #str then decode_error(str, idx, "trailing garbage") end + return res +end +return json diff --git a/Zframework/languages.lua b/Zframework/languages.lua new file mode 100644 index 000000000..7a4b2f0aa --- /dev/null +++ b/Zframework/languages.lua @@ -0,0 +1,57 @@ +local LANG={} +-- ONLY FIRST CALL MAKE SENSE +-- Create LANG.get() and LANG.addScene() +function LANG.init(defaultLang,langList,publicText,pretreatFunc) + local function _langFallback(T0,T) + for k,v in next,T0 do + if type(v)=='table' and not v.refuseCopy then-- refuseCopy: just copy pointer, not contents + if not T[k] then T[k]={} end + if type(T[k])=='table' then + _langFallback(v,T[k]) + end + elseif not T[k] then + T[k]=v + end + end + end + + -- Set public text + if publicText then + for _,L in next,langList do + for key,list in next,publicText do L[key]=list end + end + end + + -- Fallback to default language + for name,L in next,langList do + if name~=defaultLang then + _langFallback(langList[L.fallback or defaultLang],L) + end + end + + -- Custom pretreatment for each language + if pretreatFunc then + for _,L in next,langList do + pretreatFunc(L) + end + end + + function LANG.get(l) + if not langList[l] then + LOG("Wrong language: "..tostring(l)) + l=defaultLang + end + return langList[l] + end + + function LANG.addScene(name) + for _,L in next,langList do + if L.WidgetText and not L.WidgetText[name] then + L.WidgetText[name]={} + end + end + end + + function LANG.init() end +end +return LANG diff --git a/Zframework/log.lua b/Zframework/log.lua new file mode 100644 index 000000000..f1cec3cdb --- /dev/null +++ b/Zframework/log.lua @@ -0,0 +1,20 @@ +local ins=table.insert + +local logs={os.date("Techmino logs %Y/%m/%d %A")} + +local function log(message) + ins(logs,os.date("[%H:%M:%S] ")..message) +end + +local LOG=setmetatable({logs=logs},{ + __call=function(_,message) + print(message) + log(message) + end +}) + +function LOG.read() + return table.concat(logs,"\n") +end + +return LOG diff --git a/Zframework/lowcaser.txt b/Zframework/lowcaser.txt new file mode 100644 index 000000000..0a464dd1b --- /dev/null +++ b/Zframework/lowcaser.txt @@ -0,0 +1,26 @@ +A=a,B=b,C=c,D=d,E=e,F=f,G=g,H=h,I=i,J=j,K=k,L=l,M=m,N=n,O=o,P=p,Q=q,R=r,S=s,T=t,U=u,V=v,W=w,X=x,Y=y,Z=z +Μ=µ,ẞ=ß,À=à,Á=á,Â=â,Ã=ã,Ä=ä,Å=å,Æ=æ,Ç=ç,È=è,É=é,Ê=ê,Ë=ë,Ì=ì,Í=í,Î=î,Ï=ï,Ð=ð,Ñ=ñ,Ò=ò,Ó=ó,Ô=ô,Õ=õ,Ö=ö,Ø=ø,Ù=ù,Ú=ú,Û=û,Ü=ü,Ý=ý,Þ=þ,Ÿ=ÿ,Ā=ā,Ă=ă,Ą=ą,Ć=ć,Ĉ=ĉ,Ċ=ċ,Č=č,Ď=ď,Đ=đ,Ē=ē,Ĕ=ĕ,Ė=ė,Ę=ę,Ě=ě,Ĝ=ĝ,Ğ=ğ,Ġ=ġ,Ģ=ģ,Ĥ=ĥ,Ħ=ħ,Ĩ=ĩ,Ī=ī,Ĭ=ĭ,Į=į,IJ=ij,Ĵ=ĵ,Ķ=ķ,Ĺ=ĺ,Ļ=ļ,Ľ=ľ,Ŀ=ŀ,Ł=ł,Ń=ń,Ņ=ņ,Ň=ň,Ŋ=ŋ,Ō=ō,Ŏ=ŏ,Ő=ő,Œ=œ,Ŕ=ŕ,Ŗ=ŗ,Ř=ř,Ś=ś,Ŝ=ŝ,Ş=ş,Š=š,Ţ=ţ,Ť=ť,Ŧ=ŧ,Ũ=ũ,Ū=ū,Ŭ=ŭ,Ů=ů,Ű=ű,Ų=ų,Ŵ=ŵ,Ŷ=ŷ,Ź=ź,Ż=ż,Ž=ž +Ƀ=ƀ,Ƃ=ƃ,Ƅ=ƅ,Ƈ=ƈ,Ƌ=ƌ,Ƒ=ƒ,Ƕ=ƕ,Ƙ=ƙ,Ƚ=ƚ,Ƞ=ƞ,Ơ=ơ,Ƣ=ƣ,Ƥ=ƥ,Ƨ=ƨ,Ƭ=ƭ,Ư=ư,Ƴ=ƴ,Ƶ=ƶ,Ƹ=ƹ,Ƽ=ƽ,Ƿ=ƿ,DŽ=dž,LJ=lj,NJ=nj,Ǎ=ǎ,Ǐ=ǐ,Ǒ=ǒ,Ǔ=ǔ,Ǖ=ǖ,Ǘ=ǘ,Ǚ=ǚ,Ǜ=ǜ,Ǝ=ǝ,Ǟ=ǟ,Ǡ=ǡ,Ǣ=ǣ,Ǥ=ǥ,Ǧ=ǧ,Ǩ=ǩ,Ǫ=ǫ,Ǭ=ǭ,Ǯ=ǯ,DZ=dz,Ǵ=ǵ,Ǹ=ǹ,Ǻ=ǻ,Ǽ=ǽ,Ǿ=ǿ,Ȁ=ȁ,Ȃ=ȃ,Ȅ=ȅ,Ȇ=ȇ,Ȉ=ȉ,Ȋ=ȋ,Ȍ=ȍ,Ȏ=ȏ,Ȑ=ȑ,Ȓ=ȓ,Ȕ=ȕ,Ȗ=ȗ,Ș=ș,Ț=ț,Ȝ=ȝ,Ȟ=ȟ,Ȣ=ȣ,Ȥ=ȥ,Ȧ=ȧ,Ȩ=ȩ,Ȫ=ȫ,Ȭ=ȭ,Ȯ=ȯ,Ȱ=ȱ,Ȳ=ȳ,Ȼ=ȼ,Ȿ=ȿ,Ɀ=ɀ,Ɂ=ɂ,Ɇ=ɇ,Ɉ=ɉ,Ɋ=ɋ,Ɍ=ɍ,Ɏ=ɏ +Ɐ=ɐ,Ɑ=ɑ,Ɒ=ɒ,Ɓ=ɓ,Ɔ=ɔ,Ɖ=ɖ,Ɗ=ɗ,Ə=ə,Ɛ=ɛ,Ɜ=ɜ,Ɠ=ɠ,Ɡ=ɡ,Ɣ=ɣ,Ɥ=ɥ,Ɦ=ɦ,Ɨ=ɨ,Ɩ=ɩ,Ɪ=ɪ,Ɫ=ɫ,Ɬ=ɬ,Ɯ=ɯ,Ɱ=ɱ,Ɲ=ɲ,Ɵ=ɵ,Ɽ=ɽ,Ʀ=ʀ,Ʂ=ʂ,Ʃ=ʃ,Ʇ=ʇ,Ʈ=ʈ,Ʉ=ʉ,Ʊ=ʊ,Ʋ=ʋ,Ʌ=ʌ,Ʒ=ʒ,Ʝ=ʝ,Ʞ=ʞ +Ͱ=ͱ,Ͳ=ͳ,Ͷ=ͷ,Ͻ=ͻ,Ͼ=ͼ,Ͽ=ͽ,Ά=ά,Έ=έ,Ή=ή,Ί=ί,Α=α,Β=β,Γ=γ,Δ=δ,Ε=ε,Ζ=ζ,Η=η,Θ=θ,Κ=κ,Λ=λ,Ν=ν,Ξ=ξ,Ο=ο,Π=π,Ρ=ρ,Σ=σ,Τ=τ,Υ=υ,Φ=φ,Χ=χ,Ψ=ψ,Ω=ω,Ϊ=ϊ,Ϋ=ϋ,Ό=ό,Ύ=ύ,Ώ=ώ,Ϗ=ϗ,Ϙ=ϙ,Ϛ=ϛ,Ϝ=ϝ,Ϟ=ϟ,Ϡ=ϡ,Ϣ=ϣ,Ϥ=ϥ,Ϧ=ϧ,Ϩ=ϩ,Ϫ=ϫ,Ϭ=ϭ,Ϯ=ϯ,Ϲ=ϲ,Ϳ=ϳ,Ϸ=ϸ,Ϻ=ϻ +А=а,Б=б,В=в,Г=г,Д=д,Е=е,Ж=ж,З=з,И=и,Й=й,К=к,Л=л,М=м,Н=н,О=о,П=п,Р=р,С=с,Т=т,У=у,Ф=ф,Х=х,Ц=ц,Ч=ч,Ш=ш,Щ=щ,Ъ=ъ,Ы=ы,Ь=ь,Э=э,Ю=ю,Я=я,Ѐ=ѐ,Ё=ё,Ђ=ђ,Ѓ=ѓ,Є=є,Ѕ=ѕ,І=і,Ї=ї,Ј=ј,Љ=љ,Њ=њ,Ћ=ћ,Ќ=ќ,Ѝ=ѝ,Ў=ў,Џ=џ,Ѡ=ѡ,Ѣ=ѣ,Ѥ=ѥ,Ѧ=ѧ,Ѩ=ѩ,Ѫ=ѫ,Ѭ=ѭ,Ѯ=ѯ,Ѱ=ѱ,Ѳ=ѳ,Ѵ=ѵ,Ѷ=ѷ,Ѹ=ѹ,Ѻ=ѻ,Ѽ=ѽ,Ѿ=ѿ,Ҁ=ҁ,Ҋ=ҋ,Ҍ=ҍ,Ҏ=ҏ,Ґ=ґ,Ғ=ғ,Ҕ=ҕ,Җ=җ,Ҙ=ҙ,Қ=қ,Ҝ=ҝ,Ҟ=ҟ,Ҡ=ҡ,Ң=ң,Ҥ=ҥ,Ҧ=ҧ,Ҩ=ҩ,Ҫ=ҫ,Ҭ=ҭ,Ү=ү,Ұ=ұ,Ҳ=ҳ,Ҵ=ҵ,Ҷ=ҷ,Ҹ=ҹ,Һ=һ,Ҽ=ҽ,Ҿ=ҿ,Ӂ=ӂ,Ӄ=ӄ,Ӆ=ӆ,Ӈ=ӈ,Ӊ=ӊ,Ӌ=ӌ,Ӎ=ӎ,Ӏ=ӏ,Ӑ=ӑ,Ӓ=ӓ,Ӕ=ӕ,Ӗ=ӗ,Ә=ә,Ӛ=ӛ,Ӝ=ӝ,Ӟ=ӟ,Ӡ=ӡ,Ӣ=ӣ,Ӥ=ӥ,Ӧ=ӧ,Ө=ө,Ӫ=ӫ,Ӭ=ӭ,Ӯ=ӯ,Ӱ=ӱ,Ӳ=ӳ,Ӵ=ӵ,Ӷ=ӷ,Ӹ=ӹ,Ӻ=ӻ,Ӽ=ӽ,Ӿ=ӿ,Ԁ=ԁ,Ԃ=ԃ,Ԅ=ԅ,Ԇ=ԇ,Ԉ=ԉ,Ԋ=ԋ,Ԍ=ԍ,Ԏ=ԏ,Ԑ=ԑ,Ԓ=ԓ,Ԕ=ԕ,Ԗ=ԗ,Ԙ=ԙ,Ԛ=ԛ,Ԝ=ԝ,Ԟ=ԟ,Ԡ=ԡ,Ԣ=ԣ,Ԥ=ԥ,Ԧ=ԧ,Ԩ=ԩ,Ԫ=ԫ,Ԭ=ԭ,Ԯ=ԯ +Ա=ա,Բ=բ,Գ=գ,Դ=դ,Ե=ե,Զ=զ,Է=է,Ը=ը,Թ=թ,Ժ=ժ,Ի=ի,Լ=լ,Խ=խ,Ծ=ծ,Կ=կ,Հ=հ,Ձ=ձ,Ղ=ղ,Ճ=ճ,Մ=մ,Յ=յ,Ն=ն,Շ=շ,Ո=ո,Չ=չ,Պ=պ,Ջ=ջ,Ռ=ռ,Ս=ս,Վ=վ,Տ=տ,Ր=ր,Ց=ց,Ւ=ւ,Փ=փ,Ք=ք,Օ=օ,Ֆ=ֆ +Ა=ა,Ბ=ბ,Გ=გ,Დ=დ,Ე=ე,Ვ=ვ,Ზ=ზ,Თ=თ,Ი=ი,Კ=კ,Ლ=ლ,Მ=მ,Ნ=ნ,Ო=ო,Პ=პ,Ჟ=ჟ,Რ=რ,Ს=ს,Ტ=ტ,Უ=უ,Ფ=ფ,Ქ=ქ,Ღ=ღ,Ყ=ყ,Შ=შ,Ჩ=ჩ,Ც=ც,Ძ=ძ,Წ=წ,Ჭ=ჭ,Ხ=ხ,Ჯ=ჯ,Ჰ=ჰ,Ჱ=ჱ,Ჲ=ჲ,Ჳ=ჳ,Ჴ=ჴ,Ჵ=ჵ,Ჶ=ჶ,Ჷ=ჷ,Ჸ=ჸ,Ჹ=ჹ,Ჺ=ჺ,Ჽ=ჽ,Ჾ=ჾ,Ჿ=ჿ +Ᏸ=ᏸ,Ᏹ=ᏹ,Ᏺ=ᏺ,Ᏻ=ᏻ,Ᏼ=ᏼ,Ᏽ=ᏽ +Ꙋ=ᲈ,Ᵹ=ᵹ,Ᵽ=ᵽ,Ᶎ=ᶎ,Ḁ=ḁ,Ḃ=ḃ,Ḅ=ḅ,Ḇ=ḇ,Ḉ=ḉ,Ḋ=ḋ,Ḍ=ḍ,Ḏ=ḏ,Ḑ=ḑ,Ḓ=ḓ,Ḕ=ḕ,Ḗ=ḗ,Ḙ=ḙ,Ḛ=ḛ,Ḝ=ḝ,Ḟ=ḟ,Ḡ=ḡ,Ḣ=ḣ,Ḥ=ḥ,Ḧ=ḧ,Ḩ=ḩ,Ḫ=ḫ,Ḭ=ḭ,Ḯ=ḯ,Ḱ=ḱ,Ḳ=ḳ,Ḵ=ḵ,Ḷ=ḷ,Ḹ=ḹ,Ḻ=ḻ,Ḽ=ḽ,Ḿ=ḿ,Ṁ=ṁ,Ṃ=ṃ,Ṅ=ṅ,Ṇ=ṇ,Ṉ=ṉ,Ṋ=ṋ,Ṍ=ṍ,Ṏ=ṏ,Ṑ=ṑ,Ṓ=ṓ,Ṕ=ṕ,Ṗ=ṗ,Ṙ=ṙ,Ṛ=ṛ,Ṝ=ṝ,Ṟ=ṟ,Ṡ=ṡ,Ṣ=ṣ,Ṥ=ṥ,Ṧ=ṧ,Ṩ=ṩ,Ṫ=ṫ,Ṭ=ṭ,Ṯ=ṯ,Ṱ=ṱ,Ṳ=ṳ,Ṵ=ṵ,Ṷ=ṷ,Ṹ=ṹ,Ṻ=ṻ,Ṽ=ṽ,Ṿ=ṿ,Ẁ=ẁ,Ẃ=ẃ,Ẅ=ẅ,Ẇ=ẇ,Ẉ=ẉ,Ẋ=ẋ,Ẍ=ẍ,Ẏ=ẏ,Ẑ=ẑ,Ẓ=ẓ,Ẕ=ẕ,Ạ=ạ,Ả=ả,Ấ=ấ,Ầ=ầ,Ẩ=ẩ,Ẫ=ẫ,Ậ=ậ,Ắ=ắ,Ằ=ằ,Ẳ=ẳ,Ẵ=ẵ,Ặ=ặ,Ẹ=ẹ,Ẻ=ẻ,Ẽ=ẽ,Ế=ế,Ề=ề,Ể=ể,Ễ=ễ,Ệ=ệ,Ỉ=ỉ,Ị=ị,Ọ=ọ,Ỏ=ỏ,Ố=ố,Ồ=ồ,Ổ=ổ,Ỗ=ỗ,Ộ=ộ,Ớ=ớ,Ờ=ờ,Ở=ở,Ỡ=ỡ,Ợ=ợ,Ụ=ụ,Ủ=ủ,Ứ=ứ,Ừ=ừ,Ử=ử,Ữ=ữ,Ự=ự,Ỳ=ỳ,Ỵ=ỵ,Ỷ=ỷ,Ỹ=ỹ,Ỻ=ỻ,Ỽ=ỽ,Ỿ=ỿ,Ἀ=ἀ,Ἁ=ἁ,Ἂ=ἂ,Ἃ=ἃ,Ἄ=ἄ,Ἅ=ἅ,Ἆ=ἆ,Ἇ=ἇ,Ἐ=ἐ,Ἑ=ἑ,Ἒ=ἒ,Ἓ=ἓ,Ἔ=ἔ,Ἕ=ἕ,Ἠ=ἠ,Ἡ=ἡ,Ἢ=ἢ,Ἣ=ἣ,Ἤ=ἤ,Ἥ=ἥ,Ἦ=ἦ,Ἧ=ἧ,Ἰ=ἰ,Ἱ=ἱ,Ἲ=ἲ,Ἳ=ἳ,Ἴ=ἴ,Ἵ=ἵ,Ἶ=ἶ,Ἷ=ἷ,Ὀ=ὀ,Ὁ=ὁ,Ὂ=ὂ,Ὃ=ὃ,Ὄ=ὄ,Ὅ=ὅ,Ὑ=ὑ,Ὓ=ὓ,Ὕ=ὕ,Ὗ=ὗ,Ὠ=ὠ,Ὡ=ὡ,Ὢ=ὢ,Ὣ=ὣ,Ὤ=ὤ,Ὥ=ὥ,Ὦ=ὦ,Ὧ=ὧ,Ὰ=ὰ,Ά=ά,Ὲ=ὲ,Έ=έ,Ὴ=ὴ,Ή=ή,Ὶ=ὶ,Ί=ί,Ὸ=ὸ,Ό=ό,Ὺ=ὺ,Ύ=ύ,Ὼ=ὼ,Ώ=ώ,Ᾰ=ᾰ,Ᾱ=ᾱ,Ῐ=ῐ,Ῑ=ῑ,Ῠ=ῠ,Ῡ=ῡ,Ῥ=ῥ,Ⅎ=ⅎ +Ⅰ=ⅰ,Ⅱ=ⅱ,Ⅲ=ⅲ,Ⅳ=ⅳ,Ⅴ=ⅴ,Ⅵ=ⅵ,Ⅶ=ⅶ,Ⅷ=ⅷ,Ⅸ=ⅸ,Ⅹ=ⅹ,Ⅺ=ⅺ,Ⅻ=ⅻ,Ⅼ=ⅼ,Ⅽ=ⅽ,Ⅾ=ⅾ,Ⅿ=ⅿ,Ↄ=ↄ +Ⓐ=ⓐ,Ⓑ=ⓑ,Ⓒ=ⓒ,Ⓓ=ⓓ,Ⓔ=ⓔ,Ⓕ=ⓕ,Ⓖ=ⓖ,Ⓗ=ⓗ,Ⓘ=ⓘ,Ⓙ=ⓙ,Ⓚ=ⓚ,Ⓛ=ⓛ,Ⓜ=ⓜ,Ⓝ=ⓝ,Ⓞ=ⓞ,Ⓟ=ⓟ,Ⓠ=ⓠ,Ⓡ=ⓡ,Ⓢ=ⓢ,Ⓣ=ⓣ,Ⓤ=ⓤ,Ⓥ=ⓥ,Ⓦ=ⓦ,Ⓧ=ⓧ,Ⓨ=ⓨ,Ⓩ=ⓩ +Ⰰ=ⰰ,Ⰱ=ⰱ,Ⰲ=ⰲ,Ⰳ=ⰳ,Ⰴ=ⰴ,Ⰵ=ⰵ,Ⰶ=ⰶ,Ⰷ=ⰷ,Ⰸ=ⰸ,Ⰹ=ⰹ,Ⰺ=ⰺ,Ⰻ=ⰻ,Ⰼ=ⰼ,Ⰽ=ⰽ,Ⰾ=ⰾ,Ⰿ=ⰿ,Ⱀ=ⱀ,Ⱁ=ⱁ,Ⱂ=ⱂ,Ⱃ=ⱃ,Ⱄ=ⱄ,Ⱅ=ⱅ,Ⱆ=ⱆ,Ⱇ=ⱇ,Ⱈ=ⱈ,Ⱉ=ⱉ,Ⱊ=ⱊ,Ⱋ=ⱋ,Ⱌ=ⱌ,Ⱍ=ⱍ,Ⱎ=ⱎ,Ⱏ=ⱏ,Ⱐ=ⱐ,Ⱑ=ⱑ,Ⱒ=ⱒ,Ⱓ=ⱓ,Ⱔ=ⱔ,Ⱕ=ⱕ,Ⱖ=ⱖ,Ⱗ=ⱗ,Ⱘ=ⱘ,Ⱙ=ⱙ,Ⱚ=ⱚ,Ⱛ=ⱛ,Ⱜ=ⱜ,Ⱝ=ⱝ,Ⱞ=ⱞ,Ⱟ=ⱟ +Ⱡ=ⱡ,Ⱥ=ⱥ,Ⱦ=ⱦ,Ⱨ=ⱨ,Ⱪ=ⱪ,Ⱬ=ⱬ,Ⱳ=ⱳ,Ⱶ=ⱶ +Ⲁ=ⲁ,Ⲃ=ⲃ,Ⲅ=ⲅ,Ⲇ=ⲇ,Ⲉ=ⲉ,Ⲋ=ⲋ,Ⲍ=ⲍ,Ⲏ=ⲏ,Ⲑ=ⲑ,Ⲓ=ⲓ,Ⲕ=ⲕ,Ⲗ=ⲗ,Ⲙ=ⲙ,Ⲛ=ⲛ,Ⲝ=ⲝ,Ⲟ=ⲟ,Ⲡ=ⲡ,Ⲣ=ⲣ,Ⲥ=ⲥ,Ⲧ=ⲧ,Ⲩ=ⲩ,Ⲫ=ⲫ,Ⲭ=ⲭ,Ⲯ=ⲯ,Ⲱ=ⲱ,Ⲳ=ⲳ,Ⲵ=ⲵ,Ⲷ=ⲷ,Ⲹ=ⲹ,Ⲻ=ⲻ,Ⲽ=ⲽ,Ⲿ=ⲿ,Ⳁ=ⳁ,Ⳃ=ⳃ,Ⳅ=ⳅ,Ⳇ=ⳇ,Ⳉ=ⳉ,Ⳋ=ⳋ,Ⳍ=ⳍ,Ⳏ=ⳏ,Ⳑ=ⳑ,Ⳓ=ⳓ,Ⳕ=ⳕ,Ⳗ=ⳗ,Ⳙ=ⳙ,Ⳛ=ⳛ,Ⳝ=ⳝ,Ⳟ=ⳟ,Ⳡ=ⳡ,Ⳣ=ⳣ,Ⳬ=ⳬ,Ⳮ=ⳮ,Ⳳ=ⳳ +Ⴀ=ⴀ,Ⴁ=ⴁ,Ⴂ=ⴂ,Ⴃ=ⴃ,Ⴄ=ⴄ,Ⴅ=ⴅ,Ⴆ=ⴆ,Ⴇ=ⴇ,Ⴈ=ⴈ,Ⴉ=ⴉ,Ⴊ=ⴊ,Ⴋ=ⴋ,Ⴌ=ⴌ,Ⴍ=ⴍ,Ⴎ=ⴎ,Ⴏ=ⴏ,Ⴐ=ⴐ,Ⴑ=ⴑ,Ⴒ=ⴒ,Ⴓ=ⴓ,Ⴔ=ⴔ,Ⴕ=ⴕ,Ⴖ=ⴖ,Ⴗ=ⴗ,Ⴘ=ⴘ,Ⴙ=ⴙ,Ⴚ=ⴚ,Ⴛ=ⴛ,Ⴜ=ⴜ,Ⴝ=ⴝ,Ⴞ=ⴞ,Ⴟ=ⴟ,Ⴠ=ⴠ,Ⴡ=ⴡ,Ⴢ=ⴢ,Ⴣ=ⴣ,Ⴤ=ⴤ,Ⴥ=ⴥ,Ⴧ=ⴧ +Ⴭ=ⴭ,Ꙁ=ꙁ,Ꙃ=ꙃ,Ꙅ=ꙅ,Ꙇ=ꙇ,Ꙉ=ꙉ,Ꙍ=ꙍ,Ꙏ=ꙏ,Ꙑ=ꙑ,Ꙓ=ꙓ,Ꙕ=ꙕ,Ꙗ=ꙗ,Ꙙ=ꙙ,Ꙛ=ꙛ,Ꙝ=ꙝ,Ꙟ=ꙟ,Ꙡ=ꙡ,Ꙣ=ꙣ,Ꙥ=ꙥ,Ꙧ=ꙧ,Ꙩ=ꙩ,Ꙫ=ꙫ,Ꙭ=ꙭ,Ꚁ=ꚁ,Ꚃ=ꚃ,Ꚅ=ꚅ,Ꚇ=ꚇ,Ꚉ=ꚉ,Ꚋ=ꚋ,Ꚍ=ꚍ,Ꚏ=ꚏ,Ꚑ=ꚑ,Ꚓ=ꚓ,Ꚕ=ꚕ,Ꚗ=ꚗ,Ꚙ=ꚙ,Ꚛ=ꚛ,Ꜣ=ꜣ,Ꜥ=ꜥ,Ꜧ=ꜧ,Ꜩ=ꜩ,Ꜫ=ꜫ,Ꜭ=ꜭ,Ꜯ=ꜯ,Ꜳ=ꜳ,Ꜵ=ꜵ,Ꜷ=ꜷ,Ꜹ=ꜹ,Ꜻ=ꜻ,Ꜽ=ꜽ,Ꜿ=ꜿ,Ꝁ=ꝁ,Ꝃ=ꝃ,Ꝅ=ꝅ,Ꝇ=ꝇ,Ꝉ=ꝉ,Ꝋ=ꝋ,Ꝍ=ꝍ,Ꝏ=ꝏ,Ꝑ=ꝑ,Ꝓ=ꝓ,Ꝕ=ꝕ,Ꝗ=ꝗ,Ꝙ=ꝙ,Ꝛ=ꝛ,Ꝝ=ꝝ,Ꝟ=ꝟ,Ꝡ=ꝡ,Ꝣ=ꝣ,Ꝥ=ꝥ,Ꝧ=ꝧ,Ꝩ=ꝩ,Ꝫ=ꝫ,Ꝭ=ꝭ,Ꝯ=ꝯ,Ꝺ=ꝺ,Ꝼ=ꝼ,Ꝿ=ꝿ,Ꞁ=ꞁ,Ꞃ=ꞃ,Ꞅ=ꞅ,Ꞇ=ꞇ,Ꞌ=ꞌ,Ꞑ=ꞑ,Ꞓ=ꞓ,Ꞔ=ꞔ,Ꞗ=ꞗ,Ꞙ=ꞙ,Ꞛ=ꞛ,Ꞝ=ꞝ,Ꞟ=ꞟ,Ꞡ=ꞡ,Ꞣ=ꞣ,Ꞥ=ꞥ,Ꞧ=ꞧ,Ꞩ=ꞩ,Ꞵ=ꞵ,Ꞷ=ꞷ,Ꞹ=ꞹ,Ꞻ=ꞻ,Ꞽ=ꞽ,Ꞿ=ꞿ,Ꟁ=ꟁ,Ꟃ=ꟃ,Ꟈ=ꟈ,Ꟊ=ꟊ,Ꟑ=ꟑ,Ꟗ=ꟗ,Ꟙ=ꟙ,Ꟶ=ꟶ,Ꭓ=ꭓ +Ꭰ=ꭰ,Ꭱ=ꭱ,Ꭲ=ꭲ,Ꭳ=ꭳ,Ꭴ=ꭴ,Ꭵ=ꭵ,Ꭶ=ꭶ,Ꭷ=ꭷ,Ꭸ=ꭸ,Ꭹ=ꭹ,Ꭺ=ꭺ,Ꭻ=ꭻ,Ꭼ=ꭼ,Ꭽ=ꭽ,Ꭾ=ꭾ,Ꭿ=ꭿ,Ꮀ=ꮀ,Ꮁ=ꮁ,Ꮂ=ꮂ,Ꮃ=ꮃ,Ꮄ=ꮄ,Ꮅ=ꮅ,Ꮆ=ꮆ,Ꮇ=ꮇ,Ꮈ=ꮈ,Ꮉ=ꮉ,Ꮊ=ꮊ,Ꮋ=ꮋ,Ꮌ=ꮌ,Ꮍ=ꮍ,Ꮎ=ꮎ,Ꮏ=ꮏ,Ꮐ=ꮐ,Ꮑ=ꮑ,Ꮒ=ꮒ,Ꮓ=ꮓ,Ꮔ=ꮔ,Ꮕ=ꮕ,Ꮖ=ꮖ,Ꮗ=ꮗ,Ꮘ=ꮘ,Ꮙ=ꮙ,Ꮚ=ꮚ,Ꮛ=ꮛ,Ꮜ=ꮜ,Ꮝ=ꮝ,Ꮞ=ꮞ,Ꮟ=ꮟ,Ꮠ=ꮠ,Ꮡ=ꮡ,Ꮢ=ꮢ,Ꮣ=ꮣ,Ꮤ=ꮤ,Ꮥ=ꮥ,Ꮦ=ꮦ,Ꮧ=ꮧ,Ꮨ=ꮨ,Ꮩ=ꮩ,Ꮪ=ꮪ,Ꮫ=ꮫ,Ꮬ=ꮬ,Ꮭ=ꮭ,Ꮮ=ꮮ,Ꮯ=ꮯ,Ꮰ=ꮰ,Ꮱ=ꮱ,Ꮲ=ꮲ,Ꮳ=ꮳ,Ꮴ=ꮴ,Ꮵ=ꮵ,Ꮶ=ꮶ,Ꮷ=ꮷ,Ꮸ=ꮸ,Ꮹ=ꮹ,Ꮺ=ꮺ,Ꮻ=ꮻ,Ꮼ=ꮼ,Ꮽ=ꮽ,Ꮾ=ꮾ,Ꮿ=ꮿ +A=a,B=b,C=c,D=d,E=e,F=f,G=g,H=h,I=i,J=j,K=k,L=l,M=m,N=n,O=o,P=p,Q=q,R=r,S=s,T=t,U=u,V=v,W=w,X=x,Y=y,Z=z +𐐀=𐐨,𐐁=𐐩,𐐂=𐐪,𐐃=𐐫,𐐄=𐐬,𐐅=𐐭,𐐆=𐐮,𐐇=𐐯,𐐈=𐐰,𐐉=𐐱,𐐊=𐐲,𐐋=𐐳,𐐌=𐐴,𐐍=𐐵,𐐎=𐐶,𐐏=𐐷,𐐐=𐐸,𐐑=𐐹,𐐒=𐐺,𐐓=𐐻,𐐔=𐐼,𐐕=𐐽,𐐖=𐐾,𐐗=𐐿,𐐘=𐑀,𐐙=𐑁,𐐚=𐑂,𐐛=𐑃,𐐜=𐑄,𐐝=𐑅,𐐞=𐑆,𐐟=𐑇,𐐠=𐑈,𐐡=𐑉,𐐢=𐑊,𐐣=𐑋,𐐤=𐑌,𐐥=𐑍,𐐦=𐑎,𐐧=𐑏 +𐒰=𐓘,𐒱=𐓙,𐒲=𐓚,𐒳=𐓛,𐒴=𐓜,𐒵=𐓝,𐒶=𐓞,𐒷=𐓟,𐒸=𐓠,𐒹=𐓡,𐒺=𐓢,𐒻=𐓣,𐒼=𐓤,𐒽=𐓥,𐒾=𐓦,𐒿=𐓧,𐓀=𐓨,𐓁=𐓩,𐓂=𐓪,𐓃=𐓫,𐓄=𐓬,𐓅=𐓭,𐓆=𐓮,𐓇=𐓯,𐓈=𐓰,𐓉=𐓱,𐓊=𐓲,𐓋=𐓳,𐓌=𐓴,𐓍=𐓵,𐓎=𐓶,𐓏=𐓷,𐓐=𐓸,𐓑=𐓹,𐓒=𐓺,𐓓=𐓻 +𐲀=𐳀,𐲁=𐳁,𐲂=𐳂,𐲃=𐳃,𐲄=𐳄,𐲅=𐳅,𐲆=𐳆,𐲇=𐳇,𐲈=𐳈,𐲉=𐳉,𐲊=𐳊,𐲋=𐳋,𐲌=𐳌,𐲍=𐳍,𐲎=𐳎,𐲏=𐳏,𐲐=𐳐,𐲑=𐳑,𐲒=𐳒,𐲓=𐳓,𐲔=𐳔,𐲕=𐳕,𐲖=𐳖,𐲗=𐳗,𐲘=𐳘,𐲙=𐳙,𐲚=𐳚,𐲛=𐳛,𐲜=𐳜,𐲝=𐳝,𐲞=𐳞,𐲟=𐳟,𐲠=𐳠,𐲡=𐳡,𐲢=𐳢,𐲣=𐳣,𐲤=𐳤,𐲥=𐳥,𐲦=𐳦,𐲧=𐳧,𐲨=𐳨,𐲩=𐳩,𐲪=𐳪,𐲫=𐳫,𐲬=𐳬,𐲭=𐳭,𐲮=𐳮,𐲯=𐳯,𐲰=𐳰,𐲱=𐳱,𐲲=𐳲 +𑢠=𑣀,𑢡=𑣁,𑢢=𑣂,𑢣=𑣃,𑢤=𑣄,𑢥=𑣅,𑢦=𑣆,𑢧=𑣇,𑢨=𑣈,𑢩=𑣉,𑢪=𑣊,𑢫=𑣋,𑢬=𑣌,𑢭=𑣍,𑢮=𑣎,𑢯=𑣏,𑢰=𑣐,𑢱=𑣑,𑢲=𑣒,𑢳=𑣓,𑢴=𑣔,𑢵=𑣕,𑢶=𑣖,𑢷=𑣗,𑢸=𑣘,𑢹=𑣙,𑢺=𑣚,𑢻=𑣛,𑢼=𑣜,𑢽=𑣝,𑢾=𑣞,𑢿=𑣟 +𖹀=𖹠,𖹁=𖹡,𖹂=𖹢,𖹃=𖹣,𖹄=𖹤,𖹅=𖹥,𖹆=𖹦,𖹇=𖹧,𖹈=𖹨,𖹉=𖹩,𖹊=𖹪,𖹋=𖹫,𖹌=𖹬,𖹍=𖹭,𖹎=𖹮,𖹏=𖹯,𖹐=𖹰,𖹑=𖹱,𖹒=𖹲,𖹓=𖹳,𖹔=𖹴,𖹕=𖹵,𖹖=𖹶,𖹗=𖹷,𖹘=𖹸,𖹙=𖹹,𖹚=𖹺,𖹛=𖹻,𖹜=𖹼,𖹝=𖹽,𖹞=𖹾,𖹟=𖹿 +𞤀=𞤢,𞤁=𞤣,𞤂=𞤤,𞤃=𞤥,𞤄=𞤦,𞤅=𞤧,𞤆=𞤨,𞤇=𞤩,𞤈=𞤪,𞤉=𞤫,𞤊=𞤬,𞤋=𞤭,𞤌=𞤮,𞤍=𞤯,𞤎=𞤰,𞤏=𞤱,𞤐=𞤲,𞤑=𞤳,𞤒=𞤴,𞤓=𞤵,𞤔=𞤶,𞤕=𞤷,𞤖=𞤸,𞤗=𞤹,𞤘=𞤺,𞤙=𞤻,𞤚=𞤼,𞤛=𞤽,𞤜=𞤾,𞤝=𞤿,𞤞=𞥀,𞤟=𞥁,𞤠=𞥂,𞤡=𞥃 +İ=i̇,ʼN=ʼn,J̌=ǰ,Ϊ́=ΐ,Ϋ́=ΰ,ԵՒ=և,H̱=ẖ,T̈=ẗ,W̊=ẘ,Y̊=ẙ,Aʾ=ẚ,Υ̓=ὐ,Υ̓̀=ὒ,Υ̓́=ὔ,Υ̓͂=ὖ,ἈΙ=ᾀ,ἉΙ=ᾁ,ἊΙ=ᾂ,ἋΙ=ᾃ,ἌΙ=ᾄ,ἍΙ=ᾅ,ἎΙ=ᾆ,ἏΙ=ᾇ,ἨΙ=ᾐ,ἩΙ=ᾑ,ἪΙ=ᾒ,ἫΙ=ᾓ,ἬΙ=ᾔ,ἭΙ=ᾕ,ἮΙ=ᾖ,ἯΙ=ᾗ,ὨΙ=ᾠ,ὩΙ=ᾡ,ὪΙ=ᾢ,ὫΙ=ᾣ,ὬΙ=ᾤ,ὭΙ=ᾥ,ὮΙ=ᾦ,ὯΙ=ᾧ,ᾺΙ=ᾲ,ΑΙ=ᾳ,ΆΙ=ᾴ,Α͂=ᾶ,Α͂Ι=ᾷ,ῊΙ=ῂ,ΗΙ=ῃ,ΉΙ=ῄ,Η͂=ῆ,Η͂Ι=ῇ,Ϊ̀=ῒ,Ι͂=ῖ,Ϊ͂=ῗ,Ϋ̀=ῢ,Ρ̓=ῤ,Υ͂=ῦ,Ϋ͂=ῧ,ῺΙ=ῲ,ΩΙ=ῳ,ΏΙ=ῴ,Ω͂=ῶ,Ω͂Ι=ῷ,ՄՆ=ﬓ,ՄԵ=ﬔ,ՄԻ=ﬕ,ՎՆ=ﬖ,ՄԽ=ﬗ diff --git a/Zframework/mathExtend.lua b/Zframework/mathExtend.lua new file mode 100644 index 000000000..11c4b24f7 --- /dev/null +++ b/Zframework/mathExtend.lua @@ -0,0 +1,88 @@ +local MATH={} for k,v in next,math do MATH[k]=v end + +local floor,ceil=math.floor,math.ceil +local rnd=math.random +local exp=math.exp + +MATH.tau=2*math.pi +MATH.phi=(1+math.sqrt(5))/2 +MATH.inf=1/0 +MATH.nan=0/0 + +function MATH.isnan(n) + return n~=n +end + +function MATH.sign(a) + return a>0 and 1 or a<0 and -1 or 0 +end + +function MATH.roll(chance) + return rnd()<(chance or .5) +end + +function MATH.coin(a,b) + if rnd()<.5 then + return a + else + return b + end +end + +function MATH.clamp(v,low,high) + if v<=low then + return low + elseif v>=high then + return high + else + return v + end +end + +function MATH.mix(s,e,t) + return s+(e-s)*t +end + +do-- function MATH.listMix(list,t) + local clamp,mix=MATH.clamp,MATH.mix + function MATH.listMix(list,t) + local t2=(#list-1)*clamp(t,0,1)+1 + return mix(list[floor(t2)],list[ceil(t2)],t2%1) + end +end + +function MATH.expApproach(a,b,k) + return b+(a-b)*exp(-k) +end + +function MATH.distance(x1,y1,x2,y2) + return ((x1-x2)^2+(y1-y2)^2)^.5 +end + +-- By Pedro Gimeno,donated to the public domain +function MATH.pointInPolygon(x,y,poly,evenOddRule) + local x1,y1,x2,y2 + local len=#poly + x2,y2=poly[len-1],poly[len] + local wn=0 + for idx=1,len,2 do + x1,y1=x2,y2 + x2,y2=poly[idx],poly[idx+1] + if y1>y then + if y2<=y and (x1-x)*(y2-y)<(x2-x)*(y1-y) then + wn=wn+1 + end + else + if y2>y and (x1-x)*(y2-y)>(x2-x)*(y1-y) then + wn=wn-1 + end + end + end + if evenOddRule then + return wn%2~=0 + else-- non-zero winding rule + return wn~=0 + end +end + +return MATH diff --git a/Zframework/message.lua b/Zframework/message.lua new file mode 100644 index 000000000..775fed8bb --- /dev/null +++ b/Zframework/message.lua @@ -0,0 +1,153 @@ +local ins,rem=table.insert,table.remove +local max=math.max + +local mesList={} +local mesIcon={ + check=GC.DO{40,40, + {'setLW',10}, + {'setCL',0,0,0}, + {'line',4,19,15,30,36,9}, + {'setLW',6}, + {'setCL',.7,1,.6}, + {'line',5,20,15,30,35,10}, + }, + info=GC.DO{40,40, + {'setCL',.2,.25,.85}, + {'fCirc',20,20,15}, + {'setCL',1,1,1}, + {'setLW',2}, + {'dCirc',20,20,15}, + {'fRect',18,11,4,4}, + {'fRect',18,17,4,12}, + }, + broadcast=GC.DO{40,40, + {'setCL',1,1,1}, + {'fRect',2,4,36,26,3}, + {'fPoly',2,27,2,37,14,25}, + {'setCL',.5,.5,.5}, + {'fRect',6,11,4,4,1},{'fRect',14,11,19,4,1}, + {'fRect',6,19,4,4,1},{'fRect',14,19,19,4,1}, + }, + warn=GC.DO{40,40, + {'setCL',.95,.83,.4}, + {'fPoly',20.5,1,0,38,40,38}, + {'setCL',0,0,0}, + {'dPoly',20.5,1,0,38,40,38}, + {'fRect',17,10,7,18,2}, + {'fRect',17,29,7,7,2}, + {'setCL',1,1,1}, + {'fRect',18,11,5,16,2}, + {'fRect',18,30,5,5,2}, + }, + error=GC.DO{40,40, + {'setCL',.95,.3,.3}, + {'fCirc',20,20,19}, + {'setCL',0,0,0}, + {'dCirc',20,20,19}, + {'setLW',6}, + {'line',10.2,10.2,29.8,29.8}, + {'line',10.2,29.8,29.8,10.2}, + {'setLW',4}, + {'setCL',1,1,1}, + {'line',11,11,29,29}, + {'line',11,29,29,11}, + }, + music=GC.DO{40,40, + {'setLW',2}, + {'dRect',1,3,38,34,3}, + {'setLW',4}, + {'line',21,26,21,10,28,10}, + {'fElps',17,26,6,5}, + }, +} + +local MES={} +local backColors={ + check={.3,.6,.3,.7}, + broadcast={.3,.3,.6,.8}, + warn={.4,.4,.2,.9}, + error={.4,.2,.2,.9}, + music={.2,.4,.4,.9}, + other={.5,.5,.5,.7}, +} +function MES.new(icon,str,time) + local color=backColors.other + if type(icon)=='string' then + color=TABLE.shift(backColors[icon] or color) + icon=mesIcon[icon] + end + local text=GC.newText(FONT.get(30),str) + local w=math.max(text:getWidth()+(icon and 45 or 5),200)+15 + local h=math.max(text:getHeight(),46)+2 + local k=h>400 and 1/math.min(h/400,2.6) or 1 + + ins(mesList,1,{ + startTime=.26, + endTime=.26, + time=time or 3, + + color=color, + text=text,icon=icon, + w=w,h=h,k=k, + y=-h, + }) +end + +function MES.update(dt) + for i=#mesList,1,-1 do + local m=mesList[i] + if m.startTime>0 then + m.startTime=max(m.startTime-dt,0) + elseif m.time>0 then + m.time=max(m.time-dt,0) + elseif m.endTime>0 then + m.endTime=m.endTime-dt + else + rem(mesList,i) + end + if i>1 then + local _m=mesList[i-1] + m.y=MATH.expApproach(m.y,_m.y+_m.h*_m.k+3,dt*26) + else + m.y=MATH.expApproach(m.y,3,dt*26) + end + end +end + +function MES.draw() + if #mesList>0 then + GC.setLineWidth(2) + for i=1,#mesList do + local m=mesList[i] + local a=3.846*(m.endTime-m.startTime) + GC.push('transform') + GC.translate(3+SCR.safeX,m.y) + GC.scale(m.k) + + GC.setColor(m.color[1],m.color[2],m.color[3],m.color[4]*a) + GC.rectangle('fill',0,0,m.w,m.h,8) + GC.setColor(.62,.62,.62,a*.626) + GC.rectangle('line',1,1,m.w-2,m.h-2,4) + GC.setColor(1,1,1,a) + if m.icon then + GC.draw(m.icon,4,4,nil,40/m.icon:getWidth(),40/m.icon:getHeight()) + end + GC.simpY(m.text,m.icon and 50 or 10,m.h/2) + GC.pop() + end + end +end + +function MES.traceback() + local mes= + debug.traceback('',1) + :gsub(': in function',', in') + :gsub(':',' ') + :gsub('\t','') + MES.new('error',mes:sub( + mes:find("\n",2)+1, + mes:find("\n%[C%], in 'xpcall'") + ),5) +end + +return MES diff --git a/Zframework/profile.lua b/Zframework/profile.lua new file mode 100644 index 000000000..722ebfc4c --- /dev/null +++ b/Zframework/profile.lua @@ -0,0 +1,157 @@ +local clock=os.clock + +local profile={} + +local _labeled={} -- function labels +local _defined={} -- function definitions +local _tcalled={} -- time of last call +local _telapsed={}-- total execution time +local _ncalls={} -- number of calls +local _internal={}-- list of internal profiler functions + +local getInfo=debug.getinfo +function profile.hooker(event,line,info) + info=info or getInfo(2,'fnS') + local f=info.func + if _internal[f] then return end-- ignore the profiler itself + if info.name then _labeled[f]=info.name end-- get the function name if available + -- find the line definition + if not _defined[f] then + _defined[f]=info.short_src..":"..info.linedefined + _ncalls[f]=0 + _telapsed[f]=0 + end + if _tcalled[f] then + local dt=clock()-_tcalled[f] + _telapsed[f]=_telapsed[f]+dt + _tcalled[f]=nil + end + if event=='tail call' then + local prev=getInfo(3,'fnS') + profile.hooker('return',line,prev) + profile.hooker('call',line,info) + elseif event=='call' then + _tcalled[f]=clock() + else + _ncalls[f]=_ncalls[f]+1 + end +end + +--- Starts collecting data. +function profile.start() + if jit then + jit.off() + jit.flush() + end + debug.sethook(profile.hooker,'cr') +end + +--- Stops collecting data. +function profile.stop() + debug.sethook() + for f in next,_tcalled do + local dt=clock()-_tcalled[f] + _telapsed[f]=_telapsed[f]+dt + _tcalled[f]=nil + end + -- merge closures + local lookup={} + for f,d in next,_defined do + local id=(_labeled[f] or "?")..d + local f2=lookup[id] + if f2 then + _ncalls[f2]=_ncalls[f2]+(_ncalls[f] or 0) + _telapsed[f2]=_telapsed[f2]+(_telapsed[f] or 0) + _defined[f],_labeled[f]=nil,nil + _ncalls[f],_telapsed[f]=nil,nil + else + lookup[id]=f + end + end + collectgarbage() +end + +--- Resets all collected data. +function profile.reset() + for f in next,_ncalls do + _ncalls[f]=0 + _telapsed[f]=0 + _tcalled[f]=nil + end + collectgarbage() +end + +local function _comp(a,b) + local dt=_telapsed[b]-_telapsed[a] + return dt==0 and _ncalls[b]<_ncalls[a] or dt<0 +end + +--- Iterates all functions that have been called since the profile was started. +function profile.query(limit) + local t={} + for f,n in next,_ncalls do + if n>0 then + t[#t+1]=f + end + end + table.sort(t,_comp) + + if limit then while #t>limit do table.remove(t) end end + + for i,f in ipairs(t) do + local dt=0 + if _tcalled[f] then + dt=clock()-_tcalled[f] + end + t[i]={i,_labeled[f] or "?",math.floor((_telapsed[f]+dt)*1e6)/1e6,_ncalls[f],_defined[f]} + end + return t +end + +local cols={3,20,8,6,32} +function profile.report(n) + local out={} + local report=profile.query(n) + for i,row in ipairs(report) do + for j=1,5 do + local s=tostring(row[j]) + local l1,l2=#s,cols[j] + if l1l2 then + s=s:sub(l1-l2+1,l1) + end + row[j]=s + end + out[i]=table.concat(row," | ") + end + + local row=" +-----+----------------------+----------+--------+----------------------------------+ \n" + local col=" | # | Function | Time | Calls | Code | \n" + local sz=row..col..row + if #out>0 then + sz=sz.." | "..table.concat(out," | \n | ").." | \n" + end + return "\n"..sz..row +end + +local switch=false +function profile.switch() + switch=not switch + if not switch then + profile.stop() + CLIPBOARD.set(profile.report()) + profile.reset() + return false + else + profile.start() + return true + end +end + +-- store all internal profiler functions +for _,v in next,profile do + _internal[v]=type(v)=='function' +end + +return profile diff --git a/Zframework/readme.md b/Zframework/readme.md new file mode 100644 index 000000000..536a9f277 --- /dev/null +++ b/Zframework/readme.md @@ -0,0 +1,7 @@ +# Zframework (deprecated) + +This is an general app framework based on love2d, but it's deprecated now, don't use this for your own app! + +The next generation of Zframework is **Zenitha**, which is more powerful and more customizable + +[Github Repo Link](https://github.com/26F-Studio/Zenitha) diff --git a/Zframework/require.lua b/Zframework/require.lua new file mode 100644 index 000000000..6624d2984 --- /dev/null +++ b/Zframework/require.lua @@ -0,0 +1,53 @@ +package.cpath=package.cpath..';'..love.filesystem.getSaveDirectory()..'/lib/?.so;'..'?.dylib' +local loaded={} +local errorCount={} +return function(libName) + local require=require + local arch='unknown' + local success,res + if SYSTEM=='Web' then + return + end + if SYSTEM=='macOS' then + local a,b,c=package.loadlib(libName..'.dylib','luaopen_'..libName) + require=a + + if require then + success,res=pcall(require) + else + success,res=false,'package.loadlib returned nil, along with:\n[2]:\n'..b..'[3]:\n'..c + end + else + if SYSTEM=='Android' and not loaded[libName] then + local platform=(function() + local p=io.popen('uname -m') + arch=p:read('*a'):lower() + p:close() + if arch:find('v8') and not arch:find('v8l') or arch:find('64') then + return 'arm64-v8a' + else + return 'armeabi-v7a' + end + end)() + local data=love.filesystem.read('data','libAndroid/'..platform..'/'..libName..'.so') + if data then + love.filesystem.write('lib/'..libName..'.so',data) + end + loaded[libName]=true + end + success,res=pcall(require,libName) + end + if success and res then + return res + else + if not next(errorCount) then + MES.new('info',"Architecture: "..arch) + end + errorCount[libName]=(errorCount[libName] or 0)+1 + if errorCount[libName]==1 then + MES.new('error',"Cannot load "..libName..": "..tostring(res):gsub('[\128-\255]+','??')) + else + MES.new('error',("Cannot load %s (x%d)"):format(libName,errorCount[libName]),1) + end + end +end diff --git a/Zframework/scene.lua b/Zframework/scene.lua new file mode 100644 index 000000000..a9cf08ccd --- /dev/null +++ b/Zframework/scene.lua @@ -0,0 +1,223 @@ +local scenes={} + +local SCN={ + mainTouchID=nil, -- First touching ID(userdata) + swapping=false, -- If Swapping + state={ + tar=false, -- Swapping target + style=false, -- Swapping style + changeTime=false,-- Loading point + time=false, -- Full swap time + draw=false, -- Swap draw func + }, + stack={},-- Scene stack + prev=false, + cur=false, + args={},-- Arguments from previous scene + + scenes=scenes, + + -- Events + update=false, + draw=false, + mouseClick=false, + touchClick=false, + mouseDown=false, + mouseMove=false, + mouseUp=false, + wheelMoved=false, + touchDown=false, + touchUp=false, + touchMove=false, + keyDown=false, + keyUp=false, + gamepadDown=false, + gamepadUp=false, + fileDropped=false, + directoryDropped=false, + resize=false, +}-- Scene datas, returned + +function SCN.add(name,scene) + scenes[name]=scene + if scene.widgetList then + setmetatable(scene.widgetList,WIDGET.indexMeta) + end +end + +function SCN.swapUpdate(dt) + local S=SCN.state + S.time=S.time-dt + if S.time=S.changeTime then + -- Scene swapped this frame + SCN.stack[#SCN.stack]=S.tar + SCN.cur=S.tar + SCN.init(S.tar) + SCN.mainTouchID=nil + end + if S.time<0 then + SCN.swapping=false + end +end +function SCN.init(s) + love.keyboard.setTextInput(false) + + local S=scenes[s] + + WIDGET.setScrollHeight(S.widgetScrollHeight) + WIDGET.setWidgetList(S.widgetList) + SCN.enter=S.enter + SCN.leave=S.leave + SCN.mouseDown=S.mouseDown + SCN.mouseMove=S.mouseMove + SCN.mouseUp=S.mouseUp + SCN.mouseClick=S.mouseClick + SCN.wheelMoved=S.wheelMoved + SCN.touchDown=S.touchDown + SCN.touchUp=S.touchUp + SCN.touchMove=S.touchMove + SCN.touchClick=S.touchClick + SCN.keyDown=S.keyDown + SCN.keyUp=S.keyUp + SCN.gamepadDown=S.gamepadDown + SCN.gamepadUp=S.gamepadUp + SCN.fileDropped=S.fileDropped + SCN.directoryDropped=S.directoryDropped + SCN.resize=S.resize + SCN.update=S.update + SCN.draw=S.draw + if S.enter then + S.enter() + end +end +function SCN.push(tar) + table.insert(SCN.stack,tar or SCN.stack[#SCN.stack]) +end +function SCN.pop() + table.remove(SCN.stack) +end + +local swap={ + none={ + duration=0,changeTime=0, + draw=function() end + }, + flash={ + duration=.16,changeTime=.08, + draw=function() GC.clear(1,1,1) end + }, + fade={ + duration=.5,changeTime=.25, + draw=function(t) + t=t>.25 and 2-t*4 or t*4 + GC.setColor(0,0,0,t) + GC.rectangle('fill',0,0,SCR.w,SCR.h) + end + }, + fade_togame={ + duration=2,changeTime=.5, + draw=function(t) + t=t>.5 and (2-t)/1.5 or t*.5 + GC.setColor(0,0,0,t) + GC.rectangle('fill',0,0,SCR.w,SCR.h) + end + }, + slowFade={ + duration=3,changeTime=1.5, + draw=function(t) + t=t>1.5 and (3-t)/1.5 or t/1.5 + GC.setColor(0,0,0,t) + GC.rectangle('fill',0,0,SCR.w,SCR.h) + end + }, + swipeL={ + duration=.5,changeTime=.25, + draw=function(t) + t=t*2 + GC.setColor(.1,.1,.1,1-math.abs(t-.5)) + t=t*t*(3-2*t)*2-1 + GC.rectangle('fill',t*SCR.w,0,SCR.w,SCR.h) + end + }, + swipeR={ + duration=.5,changeTime=.25, + draw=function(t) + t=t*2 + GC.setColor(.1,.1,.1,1-math.abs(t-.5)) + t=t*t*(2*t-3)*2+1 + GC.rectangle('fill',t*SCR.w,0,SCR.w,SCR.h) + end + }, + swipeD={ + duration=.5,changeTime=.25, + draw=function(t) + t=t*2 + GC.setColor(.1,.1,.1,1-math.abs(t-.5)) + t=t*t*(2*t-3)*2+1 + GC.rectangle('fill',0,t*SCR.h,SCR.w,SCR.h) + end + }, +}-- Scene swapping animations +function SCN.swapTo(tar,style,...)-- Parallel scene swapping, cannot back + if scenes[tar] then + if not SCN.swapping then + SCN.prev=SCN.stack[#SCN.stack] + + style=style or 'fade' + SCN.swapping=true + SCN.args={...} + local S=SCN.state + S.tar,S.style=tar,style + S.time=swap[style].duration + S.changeTime=swap[style].changeTime + S.draw=swap[style].draw + end + else + MES.new('warn',"No Scene: "..tostring(tar)) + end +end +function SCN.go(tar,style,...)-- Normal scene swapping, can back + if scenes[tar] then + if not SCN.swapping then + SCN.push(SCN.stack[#SCN.stack] or '_') + SCN.swapTo(tar,style,...) + end + else + MES.new('warn',"No Scene: "..tar) + end +end +function SCN.back(style,...) + if SCN.swapping then return end + + -- Leave scene + if SCN.leave then + SCN.leave() + end + + -- Poll&Back to previous Scene + if #SCN.stack>1 then + SCN.pop() + SCN.swapTo(SCN.stack[#SCN.stack],style,...) + else + SCN.swapTo('quit','slowFade') + end +end +function SCN.backTo(tar,style,...) + if SCN.swapping then return end + + -- Leave scene + if SCN.leave then + SCN.leave() + end + + -- Poll&Back to previous Scene + while SCN.stack[#SCN.stack]~=tar and #SCN.stack>1 do + SCN.pop() + end + SCN.swapTo(SCN.stack[#SCN.stack],style,...) +end +function SCN.printStack() + for i=0,#SCN.stack+1 do print(SCN.stack[i] or "-------") end +end + +return SCN diff --git a/Zframework/screen.lua b/Zframework/screen.lua new file mode 100644 index 000000000..c5b0a45f7 --- /dev/null +++ b/Zframework/screen.lua @@ -0,0 +1,73 @@ +local SCR={ + w0=1280,h0=720, -- Default Screen Size + x=0,y=0, -- Up-left Coord on screen + cx=0,cy=0, -- Center Coord on screen (Center X/Y) + ex=0,ey=0, -- Down-right Coord on screen (End X/Y) + w=0,h=0, -- Fullscreen w/h for graphic functions + W=0,H=0, -- Fullscreen w/h for shader + safeX=0,safeY=0,-- Safe area + safeW=0,safeH=0,-- Safe area + rad=0, -- Radius + k=1, -- Scale size + dpi=1, -- DPI from gc.getDPIScale() + + -- Screen transformation objects + origin=love.math.newTransform(), + xOy=love.math.newTransform(), + xOy_m=love.math.newTransform(), + xOy_ul=love.math.newTransform(), + xOy_u=love.math.newTransform(), + xOy_ur=love.math.newTransform(), + xOy_l=love.math.newTransform(), + xOy_r=love.math.newTransform(), + xOy_dl=love.math.newTransform(), + xOy_d=love.math.newTransform(), + xOy_dr=love.math.newTransform(), +} +function SCR.setSize(w,h) + SCR.w0,SCR.h0=w,h +end +function SCR.resize(w,h) + SCR.w,SCR.h,SCR.dpi=w,h,love.graphics.getDPIScale() + SCR.W,SCR.H=SCR.w*SCR.dpi,SCR.h*SCR.dpi + SCR.r=h/w + SCR.rad=(w^2+h^2)^.5 + + SCR.x,SCR.y=0,0 + if SCR.r>=SCR.h0/SCR.w0 then + SCR.k=w/SCR.w0 + SCR.y=(h-SCR.h0*SCR.k)/2 + else + SCR.k=h/SCR.h0 + SCR.x=(w-SCR.w0*SCR.k)/2 + end + SCR.cx,SCR.cy=SCR.w/2,SCR.h/2 + SCR.ex,SCR.ey=SCR.w-SCR.x,SCR.h-SCR.y + SCR.safeX,SCR.safeY,SCR.safeW,SCR.safeH=love.window.getSafeArea() + + SCR.origin:setTransformation(0,0) + SCR.xOy:setTransformation(SCR.x,SCR.y,0,SCR.k) + SCR.xOy_m:setTransformation(w/2,h/2,0,SCR.k) + SCR.xOy_ul:setTransformation(0,0,0,SCR.k) + SCR.xOy_u:setTransformation(w/2,0,0,SCR.k) + SCR.xOy_ur:setTransformation(w,0,0,SCR.k) + SCR.xOy_l:setTransformation(0,h/2,0,SCR.k) + SCR.xOy_r:setTransformation(w,h/2,0,SCR.k) + SCR.xOy_dl:setTransformation(0,h,0,SCR.k) + SCR.xOy_d:setTransformation(w/2,h,0,SCR.k) + SCR.xOy_dr:setTransformation(w,h,0,SCR.k) +end +function SCR.info() + return { + ("w0,h0 : %d, %d"):format(SCR.w0,SCR.h0), + ("x,y : %d, %d"):format(SCR.x,SCR.y), + ("cx,cy : %d, %d"):format(SCR.cx,SCR.cy), + ("ex,ey : %d, %d"):format(SCR.ex,SCR.ey), + ("w,h : %d, %d"):format(SCR.w,SCR.h), + ("W,H : %d, %d"):format(SCR.W,SCR.H), + ("safeX,safeY : %d, %d"):format(SCR.safeX,SCR.safeY), + ("safeW,safeH : %d, %d"):format(SCR.safeW,SCR.safeH), + ("k,dpi,rad : %.2f, %d, %.2f"):format(SCR.k,SCR.dpi,SCR.rad), + } +end +return SCR diff --git a/Zframework/sfx.lua b/Zframework/sfx.lua new file mode 100644 index 000000000..25d0611a5 --- /dev/null +++ b/Zframework/sfx.lua @@ -0,0 +1,169 @@ +local type,rem=type,table.remove +local floor,rnd=math.floor,math.random + +local sfxList={} +local packSetting={} +local Sources={} +local volume=1 +local stereo=1 + +local noteVal={ + C=1,c=1, + D=3,d=3, + E=5,e=5, + F=6,f=6, + G=8,g=8, + A=10,a=10, + B=12,b=12, +} +local noteName={'C','C#','D','D#','E','F','F#','G','G#','A','A#','B'} +local function _getTuneHeight(tune) + local octave=tonumber(tune:sub(-1,-1)) + if octave then + local tuneHeight=noteVal[tune:sub(1,1)] + if tuneHeight then + tuneHeight=tuneHeight+(octave-1)*12 + local s=tune:sub(2,2) + if s=='s' or s=='#' then + tuneHeight=tuneHeight+1 + elseif s=='f' or s=='b' then + tuneHeight=tuneHeight-1 + end + return tuneHeight + end + end +end + +local SFX={} + +function SFX.init(list) + assert(type(list)=='table',"Initialize SFX lib with a list of filenames!") + for i=1,#list do table.insert(sfxList,list[i]) end +end +function SFX.load(path) + local c=0 + local missing=0 + for i=1,#sfxList do + local fullPath=path..sfxList[i]..'.ogg' + if love.filesystem.getInfo(fullPath) then + if Sources[sfxList[i]] then + for j=1,#Sources[sfxList[i]] do + Sources[sfxList[i]][j]:release() + end + end + Sources[sfxList[i]]={love.audio.newSource(fullPath,'static')} + c=c+1 + else + LOG("No SFX: "..sfxList[i]..'.ogg',.1) + missing=missing+1 + end + end + LOG(c.."/"..#sfxList.." SFX files loaded") + LOG(missing.." SFX files missing") + if missing>0 then + MES.new('info',missing.." SFX files missing") + end + collectgarbage() +end +function SFX.loadSample(pack) + assert(type(pack)=='table',"Usage: SFX.loadsample([table])") + assert(pack.name,"No field: name") + assert(pack.path,"No field: path") + local num=1 + while love.filesystem.getInfo(pack.path..'/'..num..'.ogg') do + Sources[pack.name..num]={love.audio.newSource(pack.path..'/'..num..'.ogg','static')} + num=num+1 + end + local base=(_getTuneHeight(pack.base) or 37)-1 + local top=base+num-1 + packSetting[pack.name]={base=base,top=top} + LOG((num-1).." "..pack.name.." samples loaded") +end + +function SFX.getCount() + return #sfxList +end +function SFX.setVol(v) + assert(type(v)=='number' and v>=0 and v<=1,'Wrong volume') + volume=v +end +function SFX.setStereo(v) + assert(type(v)=='number' and v>=0 and v<=1,'Wrong stereo') + stereo=v +end + +function SFX.getNoteName(note) + if note<1 then + return '---' + else + note=note-1 + local octave=floor(note/12)+1 + return noteName[note%12+1]..octave + end +end +function SFX.playSample(pack,...)-- vol-1, sampSet1, vol-2, sampSet2 + if ... then + local arg={...} + local vol + for i=1,#arg do + local a=arg[i] + if type(a)=='number' and a<=1 then + vol=a + else + local base=packSetting[pack].base + local top=packSetting[pack].top + local tune=type(a)=='string' and _getTuneHeight(a) or a-- Absolute tune in number + local playTune=tune+rnd(-2,2) + if playTune<=base then-- Too low notes + playTune=base+1 + elseif playTune>top then-- Too high notes + playTune=top + end + SFX.play(pack..playTune-base,vol,nil,tune-playTune) + end + end + end +end +local function _play(name,vol,pos,pitch) + if volume==0 or vol==0 then return end + local S=Sources[name]-- Source list + if not S then return end + local n=1 + while S[n]:isPlaying() do + n=n+1 + if not S[n] then + S[n]=S[1]:clone() + S[n]:seek(0) + break + end + end + S=S[n]-- AU_SRC + if S:getChannelCount()==1 then + if pos then + pos=MATH.clamp(pos,-1,1)*stereo + S:setPosition(pos,1-pos^2,0) + else + S:setPosition(0,0,0) + end + end + S:setVolume(vol^1.626) + S:setPitch(pitch and 1.0594630943592953^pitch or 1) + S:play() +end +SFX.fplay=_play-- Play sounds without apply module's volume setting +function SFX.play(name,vol,pos,pitch) + _play(name,(vol or 1)*volume,pos,pitch) +end +function SFX.reset() + for _,L in next,Sources do + if type(L)=='table' then + for i=#L,1,-1 do + if not L[i]:isPlaying() then + rem(L,i) + end + end + end + end +end + +return SFX diff --git a/Zframework/sha2.lua b/Zframework/sha2.lua new file mode 100644 index 000000000..eaf6907ff --- /dev/null +++ b/Zframework/sha2.lua @@ -0,0 +1,5675 @@ +---@diagnostic disable + +-------------------------------------------------------------------------------------------------------------------------- +-- sha2.lua +-------------------------------------------------------------------------------------------------------------------------- +-- VERSION: 12 (2022-02-23) +-- AUTHOR: Egor Skriptunoff +-- LICENSE: MIT (the same license as Lua itself) +-- URL: https://github.com/Egor-Skriptunoff/pure_lua_SHA +-- $1- +-- DESCRIPTION: +-- This module contains functions to calculate SHA digest: +-- MD5, SHA-1, +-- SHA-224, SHA-256, SHA-512/224, SHA-512/256, SHA-384, SHA-512, +-- SHA3-224, SHA3-256, SHA3-384, SHA3-512, SHAKE128, SHAKE256, +-- HMAC, +-- BLAKE2b, BLAKE2s, BLAKE2bp, BLAKE2sp, BLAKE2Xb, BLAKE2Xs, +-- BLAKE3, BLAKE3_KDF +-- Written in pure Lua. +-- Compatible with: +-- Lua 5.1, Lua 5.2, Lua 5.3, Lua 5.4, Fengari, LuaJIT 2.0/2.1 (any CPU endianness). +-- Main feature of this module: it was heavily optimized for speed. +-- For every Lua version the module contains particular implementation branch to get benefits from version-specific features. +-- - branch for Lua 5.1 (emulating bitwise operators using look-up table) +-- - branch for Lua 5.2 (using bit32/bit library), suitable for both Lua 5.2 with native "bit32" and Lua 5.1 with external library "bit" +-- - branch for Lua 5.3/5.4 (using native 64-bit bitwise operators) +-- - branch for Lua 5.3/5.4 (using native 32-bit bitwise operators) for Lua built with LUA_INT_TYPE=LUA_INT_INT +-- - branch for LuaJIT without FFI library (useful in a sandboxed environment) +-- - branch for LuaJIT x86 without FFI library (LuaJIT x86 has oddity because of lack of CPU registers) +-- - branch for LuaJIT 2.0 with FFI library (bit.* functions work only with Lua numbers) +-- - branch for LuaJIT 2.1 with FFI library (bit.* functions can work with "int64_t" arguments) +-- $1- +-- $1- +-- USAGE: +-- Input data should be provided as a binary string: either as a whole string or as a sequence of substrings (chunk-by-chunk loading, total length < 9*10^15 bytes). +-- Result (SHA digest) is returned in hexadecimal representation as a string of lowercase hex digits. +-- Simplest usage example: +-- local sha = require("sha2") +-- local your_hash = sha.sha256("your string") +-- See file "sha2_test.lua" for more examples. +-- $1- +-- $1- +-- CHANGELOG: +-- version date description +-- ------- ---------- ----------- +-- 12 2022-02-23 Now works in Luau (but NOT optimized for speed) +-- 11 2022-01-09 BLAKE3 added +-- 10 2022-01-02 BLAKE2 functions added +-- 9 2020-05-10 Now works in OpenWrt's Lua (dialect of Lua 5.1 with "double" + "invisible int32") +-- 8 2019-09-03 SHA-3 functions added +-- 7 2019-03-17 Added functions to convert to/from base64 +-- 6 2018-11-12 HMAC added +-- 5 2018-11-10 SHA-1 added +-- 4 2018-11-03 MD5 added +-- 3 2018-11-02 Bug fixed: incorrect hashing of long (2 GByte) data streams on Lua 5.3/5.4 built with "int32" integers +-- 2 2018-10-07 Decreased module loading time in Lua 5.1 implementation branch (thanks to Peter Melnichenko for giving a hint) +-- 1 2018-10-06 First release (only SHA-2 functions) +----------------------------------------------------------------------------- + + +local print_debug_messages = false -- set to true to view some messages about your system's abilities and implementation branch chosen for your system + +local unpack, table_concat, byte, char, string_rep, sub, gsub, gmatch, string_format, floor, ceil, math_min, math_max, tonumber, type, math_huge = + table.unpack or unpack, table.concat, string.byte, string.char, string.rep, string.sub, string.gsub, string.gmatch, string.format, math.floor, math.ceil, math.min, math.max, tonumber, type, math.huge + + +-------------------------------------------------------------------------------- +-- EXAMINING YOUR SYSTEM +-------------------------------------------------------------------------------- + +local function get_precision(one) + -- "one" must be either float 1.0 or integer 1 + -- returns bits_precision, is_integer + -- This function works correctly with all floating point datatypes (including non-IEEE-754) + local k, n, m, prev_n = 0, one, one + while true do + k, prev_n, n, m = k + 1, n, n + n + 1, m + m + k % 2 + if k > 256 or n - (n - 1) ~= 1 or m - (m - 1) ~= 1 or n == m then + return k, false -- floating point datatype + elseif n == prev_n then + return k, true -- integer datatype + end + end +end + +-- Make sure Lua has "double" numbers +local x = 2/3 +local Lua_has_double = x * 5 > 3 and x * 4 < 3 and get_precision(1.0) >= 53 +assert(Lua_has_double, "at least 53-bit floating point numbers are required") + +-- Q: +-- SHA2 was designed for FPU-less machines. +-- So, why floating point numbers are needed for this module? +-- A: +-- 53-bit "double" numbers are useful to calculate "magic numbers" used in SHA. +-- I prefer to write 50 LOC "magic numbers calculator" instead of storing more than 200 constants explicitly in this source file. + +local int_prec, Lua_has_integers = get_precision(1) +local Lua_has_int64 = Lua_has_integers and int_prec == 64 +local Lua_has_int32 = Lua_has_integers and int_prec == 32 +assert(Lua_has_int64 or Lua_has_int32 or not Lua_has_integers, "Lua integers must be either 32-bit or 64-bit") + +-- Q: +-- Does it mean that almost all non-standard configurations are not supported? +-- A: +-- Yes. Sorry, too many problems to support all possible Lua numbers configurations. +-- Lua 5.1/5.2 with "int32" will not work. +-- Lua 5.1/5.2 with "int64" will not work. +-- Lua 5.1/5.2 with "int128" will not work. +-- Lua 5.1/5.2 with "float" will not work. +-- Lua 5.1/5.2 with "double" is OK. (default config for Lua 5.1, Lua 5.2, LuaJIT) +-- Lua 5.3/5.4 with "int32" + "float" will not work. +-- Lua 5.3/5.4 with "int64" + "float" will not work. +-- Lua 5.3/5.4 with "int128" + "float" will not work. +-- Lua 5.3/5.4 with "int32" + "double" is OK. (config used by Fengari) +-- Lua 5.3/5.4 with "int64" + "double" is OK. (default config for Lua 5.3, Lua 5.4) +-- Lua 5.3/5.4 with "int128" + "double" will not work. +-- Using floating point numbers better than "double" instead of "double" is OK (non-IEEE-754 floating point implementation are allowed). +-- Using "int128" instead of "int64" is not OK: "int128" would require different branch of implementation for optimized SHA512. + +-- Check for LuaJIT and 32-bit bitwise libraries +local is_LuaJIT = ({false, [1] = true})[1] and _VERSION ~= "Luau" and (type(jit) ~= "table" or jit.version_num >= 20000) -- LuaJIT 1.x.x and Luau are treated as vanilla Lua 5.1/5.2 +local is_LuaJIT_21 -- LuaJIT 2.1+ +local LuaJIT_arch +local ffi -- LuaJIT FFI library (as a table) +local b -- 32-bit bitwise library (as a table) +local library_name + +if is_LuaJIT then + -- Assuming "bit" library is always available on LuaJIT + b = require"bit" + library_name = "bit" + -- "ffi" is intentionally disabled on some systems for safety reason + local LuaJIT_has_FFI, result = pcall(require, "ffi") + if LuaJIT_has_FFI then + ffi = result + end + is_LuaJIT_21 = not not loadstring"b=0b0" + LuaJIT_arch = type(jit) == "table" and jit.arch or ffi and ffi.arch or nil +else + -- For vanilla Lua, "bit"/"bit32" libraries are searched in global namespace only. No attempt is made to load a library if it's not loaded yet. + for _, libname in ipairs(_VERSION == "Lua 5.2" and {"bit32", "bit"} or {"bit", "bit32"}) do + if type(_G[libname]) == "table" and _G[libname].bxor then + b = _G[libname] + library_name = libname + break + end + end +end + +-------------------------------------------------------------------------------- +-- You can disable here some of your system's abilities (for testing purposes) +-------------------------------------------------------------------------------- +-- is_LuaJIT = nil +-- is_LuaJIT_21 = nil +-- ffi = nil +-- Lua_has_int32 = nil +-- Lua_has_int64 = nil +-- b, library_name = nil +-------------------------------------------------------------------------------- + +if print_debug_messages then + -- Printing list of abilities of your system + print("Abilities:") + print(" Lua version: "..(is_LuaJIT and "LuaJIT "..(is_LuaJIT_21 and "2.1 " or "2.0 ")..(LuaJIT_arch or "")..(ffi and " with FFI" or " without FFI") or _VERSION)) + print(" Integer bitwise operators: "..(Lua_has_int64 and "int64" or Lua_has_int32 and "int32" or "no")) + print(" 32-bit bitwise library: "..(library_name or " not found")) +end + +-- Selecting the most suitable implementation for given set of abilities +local method, branch +if is_LuaJIT and ffi then + method = "Using 'ffi' library of LuaJIT" + branch = "FFI" +elseif is_LuaJIT then + method = "Using special code for sandboxed LuaJIT (no FFI)" + branch = "LJ" +elseif Lua_has_int64 then + method = "Using native int64 bitwise operators" + branch = "INT64" +elseif Lua_has_int32 then + method = "Using native int32 bitwise operators" + branch = "INT32" +elseif library_name then -- when bitwise library is available (Lua 5.2 with native library "bit32" or Lua 5.1 with external library "bit") + method = "Using '"..library_name.."' library" + branch = "LIB32" +else + method = "Emulating bitwise operators using look-up table" + branch = "EMUL" +end + +if print_debug_messages then + -- Printing the implementation selected to be used on your system + print("Implementation selected:") + print(" "..method) +end + + +-------------------------------------------------------------------------------- +-- BASIC 32-BIT BITWISE FUNCTIONS +-------------------------------------------------------------------------------- + +local AND, OR, XOR, SHL, SHR, ROL, ROR, NOT, NORM, HEX, XOR_BYTE +-- Only low 32 bits of function arguments matter, high bits are ignored +-- The result of all functions (except HEX) is an integer inside "correct range": +-- for "bit" library: (-2^31)..(2^31-1) +-- for "bit32" library: 0..(2^32-1) + +if branch == "FFI" or branch == "LJ" or branch == "LIB32" then + + -- Your system has 32-bit bitwise library (either "bit" or "bit32") + + AND = b.band -- 2 arguments + OR = b.bor -- 2 arguments + XOR = b.bxor -- 2..5 arguments + SHL = b.lshift -- second argument is integer 0..31 + SHR = b.rshift -- second argument is integer 0..31 + ROL = b.rol or b.lrotate -- second argument is integer 0..31 + ROR = b.ror or b.rrotate -- second argument is integer 0..31 + NOT = b.bnot -- only for LuaJIT + NORM = b.tobit -- only for LuaJIT + HEX = b.tohex -- returns string of 8 lowercase hexadecimal digits + assert(AND and OR and XOR and SHL and SHR and ROL and ROR and NOT, "Library '"..library_name.."' is incomplete") + XOR_BYTE = XOR -- XOR of two bytes (0..255) + +elseif branch == "EMUL" then + + -- Emulating 32-bit bitwise operations using 53-bit floating point arithmetic + + function SHL(x, n) + return (x * 2^n) % 2^32 + end + + function SHR(x, n) + x = x % 2^32 / 2^n + return x - x % 1 + end + + function ROL(x, n) + x = x % 2^32 * 2^n + local r = x % 2^32 + return r + (x - r) / 2^32 + end + + function ROR(x, n) + x = x % 2^32 / 2^n + local r = x % 1 + return r * 2^32 + (x - r) + end + + local AND_of_two_bytes = {[0] = 0} -- look-up table (256*256 entries) + local idx = 0 + for y = 0, 127 * 256, 256 do + for x = y, y + 127 do + x = AND_of_two_bytes[x] * 2 + AND_of_two_bytes[idx] = x + AND_of_two_bytes[idx + 1] = x + AND_of_two_bytes[idx + 256] = x + AND_of_two_bytes[idx + 257] = x + 1 + idx = idx + 2 + end + idx = idx + 256 + end + + local function and_or_xor(x, y, operation) + -- operation: nil = AND, 1 = OR, 2 = XOR + local x0 = x % 2^32 + local y0 = y % 2^32 + local rx = x0 % 256 + local ry = y0 % 256 + local res = AND_of_two_bytes[rx + ry * 256] + x = x0 - rx + y = (y0 - ry) / 256 + rx = x % 65536 + ry = y % 256 + res = res + AND_of_two_bytes[rx + ry] * 256 + x = (x - rx) / 256 + y = (y - ry) / 256 + rx = x % 65536 + y % 256 + res = res + AND_of_two_bytes[rx] * 65536 + res = res + AND_of_two_bytes[(x + y - rx) / 256] * 16777216 + if operation then + res = x0 + y0 - operation * res + end + return res + end + + function AND(x, y) + return and_or_xor(x, y) + end + + function OR(x, y) + return and_or_xor(x, y, 1) + end + + function XOR(x, y, z, t, u) -- 2..5 arguments + if z then + if t then + if u then + t = and_or_xor(t, u, 2) + end + z = and_or_xor(z, t, 2) + end + y = and_or_xor(y, z, 2) + end + return and_or_xor(x, y, 2) + end + + function XOR_BYTE(x, y) + return x + y - 2 * AND_of_two_bytes[x + y * 256] + end + +end + +HEX = HEX + or + pcall(string_format, "%x", 2^31) and + function (x) -- returns string of 8 lowercase hexadecimal digits + return string_format("%08x", x % 4294967296) + end + or + function (x) -- for OpenWrt's dialect of Lua + return string_format("%08x", (x + 2^31) % 2^32 - 2^31) + end + +local function XORA5(x, y) + return XOR(x, y or 0xA5A5A5A5) % 4294967296 +end + +local function create_array_of_lanes() + return {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} +end + + +-------------------------------------------------------------------------------- +-- CREATING OPTIMIZED INNER LOOP +-------------------------------------------------------------------------------- + +-- Inner loop functions +local sha256_feed_64, sha512_feed_128, md5_feed_64, sha1_feed_64, keccak_feed, blake2s_feed_64, blake2b_feed_128, blake3_feed_64 + +-- Arrays of SHA-2 "magic numbers" (in "INT64" and "FFI" branches "*_lo" arrays contain 64-bit values) +local sha2_K_lo, sha2_K_hi, sha2_H_lo, sha2_H_hi, sha3_RC_lo, sha3_RC_hi = {}, {}, {}, {}, {}, {} +local sha2_H_ext256 = {[224] = {}, [256] = sha2_H_hi} +local sha2_H_ext512_lo, sha2_H_ext512_hi = {[384] = {}, [512] = sha2_H_lo}, {[384] = {}, [512] = sha2_H_hi} +local md5_K, md5_sha1_H = {}, {0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0} +local md5_next_shift = {0, 0, 0, 0, 0, 0, 0, 0, 28, 25, 26, 27, 0, 0, 10, 9, 11, 12, 0, 15, 16, 17, 18, 0, 20, 22, 23, 21} +local HEX64, lanes_index_base -- defined only for branches that internally use 64-bit integers: "INT64" and "FFI" +local common_W = {} -- temporary table shared between all calculations (to avoid creating new temporary table every time) +local common_W_blake2b, common_W_blake2s, v_for_blake2s_feed_64 = common_W, common_W, {} +local K_lo_modulo, hi_factor, hi_factor_keccak = 4294967296, 0, 0 +local sigma = { + { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }, + { 15, 11, 5, 9, 10, 16, 14, 7, 2, 13, 1, 3, 12, 8, 6, 4 }, + { 12, 9, 13, 1, 6, 3, 16, 14, 11, 15, 4, 7, 8, 2, 10, 5 }, + { 8, 10, 4, 2, 14, 13, 12, 15, 3, 7, 6, 11, 5, 1, 16, 9 }, + { 10, 1, 6, 8, 3, 5, 11, 16, 15, 2, 12, 13, 7, 9, 4, 14 }, + { 3, 13, 7, 11, 1, 12, 9, 4, 5, 14, 8, 6, 16, 15, 2, 10 }, + { 13, 6, 2, 16, 15, 14, 5, 11, 1, 8, 7, 4, 10, 3, 9, 12 }, + { 14, 12, 8, 15, 13, 2, 4, 10, 6, 1, 16, 5, 9, 7, 3, 11 }, + { 7, 16, 15, 10, 12, 4, 1, 9, 13, 3, 14, 8, 2, 5, 11, 6 }, + { 11, 3, 9, 5, 8, 7, 2, 6, 16, 12, 10, 15, 4, 13, 14, 1 }, +}; sigma[11], sigma[12] = sigma[1], sigma[2] +local perm_blake3 = { + 1, 3, 4, 11, 13, 10, 12, 6, + 1, 3, 4, 11, 13, 10, + 2, 7, 5, 8, 14, 15, 16, 9, + 2, 7, 5, 8, 14, 15, +} + +local function build_keccak_format(elem) + local keccak_format = {} + for _, size in ipairs{1, 9, 13, 17, 18, 21} do + keccak_format[size] = "<"..string_rep(elem, size) + end + return keccak_format +end + + +if branch == "FFI" then + + local common_W_FFI_int32 = ffi.new("int32_t[?]", 80) -- 64 is enough for SHA256, but 80 is needed for SHA-1 + common_W_blake2s = common_W_FFI_int32 + v_for_blake2s_feed_64 = ffi.new("int32_t[?]", 16) + perm_blake3 = ffi.new("uint8_t[?]", #perm_blake3 + 1, 0, unpack(perm_blake3)) + for j = 1, 10 do + sigma[j] = ffi.new("uint8_t[?]", #sigma[j] + 1, 0, unpack(sigma[j])) + end; sigma[11], sigma[12] = sigma[1], sigma[2] + + + -- SHA256 implementation for "LuaJIT with FFI" branch + + function sha256_feed_64(H, str, offs, size) + -- offs >= 0, size >= 0, size is multiple of 64 + local W, K = common_W_FFI_int32, sha2_K_hi + for pos = offs, offs + size - 1, 64 do + for j = 0, 15 do + pos = pos + 4 + local a, b, c, d = byte(str, pos - 3, pos) -- slow, but doesn't depend on endianness + W[j] = OR(SHL(a, 24), SHL(b, 16), SHL(c, 8), d) + end + for j = 16, 63 do + local a, b = W[j-15], W[j-2] + W[j] = NORM( XOR(ROR(a, 7), ROL(a, 14), SHR(a, 3)) + XOR(ROL(b, 15), ROL(b, 13), SHR(b, 10)) + W[j-7] + W[j-16] ) + end + local a, b, c, d, e, f, g, h = H[1], H[2], H[3], H[4], H[5], H[6], H[7], H[8] + for j = 0, 63, 8 do -- Thanks to Peter Cawley for this workaround (unroll the loop to avoid "PHI shuffling too complex" due to PHIs overlap) + local z = NORM( XOR(g, AND(e, XOR(f, g))) + XOR(ROR(e, 6), ROR(e, 11), ROL(e, 7)) + (W[j] + K[j+1] + h) ) + h, g, f, e = g, f, e, NORM( d + z ) + d, c, b, a = c, b, a, NORM( XOR(AND(a, XOR(b, c)), AND(b, c)) + XOR(ROR(a, 2), ROR(a, 13), ROL(a, 10)) + z ) + z = NORM( XOR(g, AND(e, XOR(f, g))) + XOR(ROR(e, 6), ROR(e, 11), ROL(e, 7)) + (W[j+1] + K[j+2] + h) ) + h, g, f, e = g, f, e, NORM( d + z ) + d, c, b, a = c, b, a, NORM( XOR(AND(a, XOR(b, c)), AND(b, c)) + XOR(ROR(a, 2), ROR(a, 13), ROL(a, 10)) + z ) + z = NORM( XOR(g, AND(e, XOR(f, g))) + XOR(ROR(e, 6), ROR(e, 11), ROL(e, 7)) + (W[j+2] + K[j+3] + h) ) + h, g, f, e = g, f, e, NORM( d + z ) + d, c, b, a = c, b, a, NORM( XOR(AND(a, XOR(b, c)), AND(b, c)) + XOR(ROR(a, 2), ROR(a, 13), ROL(a, 10)) + z ) + z = NORM( XOR(g, AND(e, XOR(f, g))) + XOR(ROR(e, 6), ROR(e, 11), ROL(e, 7)) + (W[j+3] + K[j+4] + h) ) + h, g, f, e = g, f, e, NORM( d + z ) + d, c, b, a = c, b, a, NORM( XOR(AND(a, XOR(b, c)), AND(b, c)) + XOR(ROR(a, 2), ROR(a, 13), ROL(a, 10)) + z ) + z = NORM( XOR(g, AND(e, XOR(f, g))) + XOR(ROR(e, 6), ROR(e, 11), ROL(e, 7)) + (W[j+4] + K[j+5] + h) ) + h, g, f, e = g, f, e, NORM( d + z ) + d, c, b, a = c, b, a, NORM( XOR(AND(a, XOR(b, c)), AND(b, c)) + XOR(ROR(a, 2), ROR(a, 13), ROL(a, 10)) + z ) + z = NORM( XOR(g, AND(e, XOR(f, g))) + XOR(ROR(e, 6), ROR(e, 11), ROL(e, 7)) + (W[j+5] + K[j+6] + h) ) + h, g, f, e = g, f, e, NORM( d + z ) + d, c, b, a = c, b, a, NORM( XOR(AND(a, XOR(b, c)), AND(b, c)) + XOR(ROR(a, 2), ROR(a, 13), ROL(a, 10)) + z ) + z = NORM( XOR(g, AND(e, XOR(f, g))) + XOR(ROR(e, 6), ROR(e, 11), ROL(e, 7)) + (W[j+6] + K[j+7] + h) ) + h, g, f, e = g, f, e, NORM( d + z ) + d, c, b, a = c, b, a, NORM( XOR(AND(a, XOR(b, c)), AND(b, c)) + XOR(ROR(a, 2), ROR(a, 13), ROL(a, 10)) + z ) + z = NORM( XOR(g, AND(e, XOR(f, g))) + XOR(ROR(e, 6), ROR(e, 11), ROL(e, 7)) + (W[j+7] + K[j+8] + h) ) + h, g, f, e = g, f, e, NORM( d + z ) + d, c, b, a = c, b, a, NORM( XOR(AND(a, XOR(b, c)), AND(b, c)) + XOR(ROR(a, 2), ROR(a, 13), ROL(a, 10)) + z ) + end + H[1], H[2], H[3], H[4] = NORM(a + H[1]), NORM(b + H[2]), NORM(c + H[3]), NORM(d + H[4]) + H[5], H[6], H[7], H[8] = NORM(e + H[5]), NORM(f + H[6]), NORM(g + H[7]), NORM(h + H[8]) + end + end + + + local common_W_FFI_int64 = ffi.new("int64_t[?]", 80) + common_W_blake2b = common_W_FFI_int64 + local int64 = ffi.typeof"int64_t" + local int32 = ffi.typeof"int32_t" + local uint32 = ffi.typeof"uint32_t" + hi_factor = int64(2^32) + + if is_LuaJIT_21 then -- LuaJIT 2.1 supports bitwise 64-bit operations + + local AND64, OR64, XOR64, NOT64, SHL64, SHR64, ROL64, ROR64 -- introducing synonyms for better code readability + = AND, OR, XOR, NOT, SHL, SHR, ROL, ROR + HEX64 = HEX + + + -- BLAKE2b implementation for "LuaJIT 2.1 + FFI" branch + + do + local v = ffi.new("int64_t[?]", 16) + local W = common_W_blake2b + + local function G(a, b, c, d, k1, k2) + local va, vb, vc, vd = v[a], v[b], v[c], v[d] + va = W[k1] + (va + vb) + vd = ROR64(XOR64(vd, va), 32) + vc = vc + vd + vb = ROR64(XOR64(vb, vc), 24) + va = W[k2] + (va + vb) + vd = ROR64(XOR64(vd, va), 16) + vc = vc + vd + vb = ROL64(XOR64(vb, vc), 1) + v[a], v[b], v[c], v[d] = va, vb, vc, vd + end + + function blake2b_feed_128(H, _, str, offs, size, bytes_compressed, last_block_size, is_last_node) + -- offs >= 0, size >= 0, size is multiple of 128 + local h1, h2, h3, h4, h5, h6, h7, h8 = H[1], H[2], H[3], H[4], H[5], H[6], H[7], H[8] + for pos = offs, offs + size - 1, 128 do + if str then + for j = 1, 16 do + pos = pos + 8 + local a, b, c, d, e, f, g, h = byte(str, pos - 7, pos) + W[j] = XOR64(OR(SHL(h, 24), SHL(g, 16), SHL(f, 8), e) * int64(2^32), uint32(int32(OR(SHL(d, 24), SHL(c, 16), SHL(b, 8), a)))) + end + end + v[0x0], v[0x1], v[0x2], v[0x3], v[0x4], v[0x5], v[0x6], v[0x7] = h1, h2, h3, h4, h5, h6, h7, h8 + v[0x8], v[0x9], v[0xA], v[0xB], v[0xD], v[0xE], v[0xF] = sha2_H_lo[1], sha2_H_lo[2], sha2_H_lo[3], sha2_H_lo[4], sha2_H_lo[6], sha2_H_lo[7], sha2_H_lo[8] + bytes_compressed = bytes_compressed + (last_block_size or 128) + v[0xC] = XOR64(sha2_H_lo[5], bytes_compressed) -- t0 = low_8_bytes(bytes_compressed) + -- t1 = high_8_bytes(bytes_compressed) = 0, message length is always below 2^53 bytes + if last_block_size then -- flag f0 + v[0xE] = NOT64(v[0xE]) + end + if is_last_node then -- flag f1 + v[0xF] = NOT64(v[0xF]) + end + for j = 1, 12 do + local row = sigma[j] + G(0, 4, 8, 12, row[ 1], row[ 2]) + G(1, 5, 9, 13, row[ 3], row[ 4]) + G(2, 6, 10, 14, row[ 5], row[ 6]) + G(3, 7, 11, 15, row[ 7], row[ 8]) + G(0, 5, 10, 15, row[ 9], row[10]) + G(1, 6, 11, 12, row[11], row[12]) + G(2, 7, 8, 13, row[13], row[14]) + G(3, 4, 9, 14, row[15], row[16]) + end + h1 = XOR64(h1, v[0x0], v[0x8]) + h2 = XOR64(h2, v[0x1], v[0x9]) + h3 = XOR64(h3, v[0x2], v[0xA]) + h4 = XOR64(h4, v[0x3], v[0xB]) + h5 = XOR64(h5, v[0x4], v[0xC]) + h6 = XOR64(h6, v[0x5], v[0xD]) + h7 = XOR64(h7, v[0x6], v[0xE]) + h8 = XOR64(h8, v[0x7], v[0xF]) + end + H[1], H[2], H[3], H[4], H[5], H[6], H[7], H[8] = h1, h2, h3, h4, h5, h6, h7, h8 + return bytes_compressed + end + + end + + + -- SHA-3 implementation for "LuaJIT 2.1 + FFI" branch + + local arr64_t = ffi.typeof"int64_t[?]" + -- lanes array is indexed from 0 + lanes_index_base = 0 + hi_factor_keccak = int64(2^32) + + function create_array_of_lanes() + return arr64_t(30) -- 25 + 5 for temporary usage + end + + function keccak_feed(lanes, _, str, offs, size, block_size_in_bytes) + -- offs >= 0, size >= 0, size is multiple of block_size_in_bytes, block_size_in_bytes is positive multiple of 8 + local RC = sha3_RC_lo + local qwords_qty = SHR(block_size_in_bytes, 3) + for pos = offs, offs + size - 1, block_size_in_bytes do + for j = 0, qwords_qty - 1 do + pos = pos + 8 + local h, g, f, e, d, c, b, a = byte(str, pos - 7, pos) -- slow, but doesn't depend on endianness + lanes[j] = XOR64(lanes[j], OR64(OR(SHL(a, 24), SHL(b, 16), SHL(c, 8), d) * int64(2^32), uint32(int32(OR(SHL(e, 24), SHL(f, 16), SHL(g, 8), h))))) + end + for round_idx = 1, 24 do + for j = 0, 4 do + lanes[25 + j] = XOR64(lanes[j], lanes[j+5], lanes[j+10], lanes[j+15], lanes[j+20]) + end + local D = XOR64(lanes[25], ROL64(lanes[27], 1)) + lanes[1], lanes[6], lanes[11], lanes[16] = ROL64(XOR64(D, lanes[6]), 44), ROL64(XOR64(D, lanes[16]), 45), ROL64(XOR64(D, lanes[1]), 1), ROL64(XOR64(D, lanes[11]), 10) + lanes[21] = ROL64(XOR64(D, lanes[21]), 2) + D = XOR64(lanes[26], ROL64(lanes[28], 1)) + lanes[2], lanes[7], lanes[12], lanes[22] = ROL64(XOR64(D, lanes[12]), 43), ROL64(XOR64(D, lanes[22]), 61), ROL64(XOR64(D, lanes[7]), 6), ROL64(XOR64(D, lanes[2]), 62) + lanes[17] = ROL64(XOR64(D, lanes[17]), 15) + D = XOR64(lanes[27], ROL64(lanes[29], 1)) + lanes[3], lanes[8], lanes[18], lanes[23] = ROL64(XOR64(D, lanes[18]), 21), ROL64(XOR64(D, lanes[3]), 28), ROL64(XOR64(D, lanes[23]), 56), ROL64(XOR64(D, lanes[8]), 55) + lanes[13] = ROL64(XOR64(D, lanes[13]), 25) + D = XOR64(lanes[28], ROL64(lanes[25], 1)) + lanes[4], lanes[14], lanes[19], lanes[24] = ROL64(XOR64(D, lanes[24]), 14), ROL64(XOR64(D, lanes[19]), 8), ROL64(XOR64(D, lanes[4]), 27), ROL64(XOR64(D, lanes[14]), 39) + lanes[9] = ROL64(XOR64(D, lanes[9]), 20) + D = XOR64(lanes[29], ROL64(lanes[26], 1)) + lanes[5], lanes[10], lanes[15], lanes[20] = ROL64(XOR64(D, lanes[10]), 3), ROL64(XOR64(D, lanes[20]), 18), ROL64(XOR64(D, lanes[5]), 36), ROL64(XOR64(D, lanes[15]), 41) + lanes[0] = XOR64(D, lanes[0]) + lanes[0], lanes[1], lanes[2], lanes[3], lanes[4] = XOR64(lanes[0], AND64(NOT64(lanes[1]), lanes[2]), RC[round_idx]), XOR64(lanes[1], AND64(NOT64(lanes[2]), lanes[3])), XOR64(lanes[2], AND64(NOT64(lanes[3]), lanes[4])), XOR64(lanes[3], AND64(NOT64(lanes[4]), lanes[0])), XOR64(lanes[4], AND64(NOT64(lanes[0]), lanes[1])) + lanes[5], lanes[6], lanes[7], lanes[8], lanes[9] = XOR64(lanes[8], AND64(NOT64(lanes[9]), lanes[5])), XOR64(lanes[9], AND64(NOT64(lanes[5]), lanes[6])), XOR64(lanes[5], AND64(NOT64(lanes[6]), lanes[7])), XOR64(lanes[6], AND64(NOT64(lanes[7]), lanes[8])), XOR64(lanes[7], AND64(NOT64(lanes[8]), lanes[9])) + lanes[10], lanes[11], lanes[12], lanes[13], lanes[14] = XOR64(lanes[11], AND64(NOT64(lanes[12]), lanes[13])), XOR64(lanes[12], AND64(NOT64(lanes[13]), lanes[14])), XOR64(lanes[13], AND64(NOT64(lanes[14]), lanes[10])), XOR64(lanes[14], AND64(NOT64(lanes[10]), lanes[11])), XOR64(lanes[10], AND64(NOT64(lanes[11]), lanes[12])) + lanes[15], lanes[16], lanes[17], lanes[18], lanes[19] = XOR64(lanes[19], AND64(NOT64(lanes[15]), lanes[16])), XOR64(lanes[15], AND64(NOT64(lanes[16]), lanes[17])), XOR64(lanes[16], AND64(NOT64(lanes[17]), lanes[18])), XOR64(lanes[17], AND64(NOT64(lanes[18]), lanes[19])), XOR64(lanes[18], AND64(NOT64(lanes[19]), lanes[15])) + lanes[20], lanes[21], lanes[22], lanes[23], lanes[24] = XOR64(lanes[22], AND64(NOT64(lanes[23]), lanes[24])), XOR64(lanes[23], AND64(NOT64(lanes[24]), lanes[20])), XOR64(lanes[24], AND64(NOT64(lanes[20]), lanes[21])), XOR64(lanes[20], AND64(NOT64(lanes[21]), lanes[22])), XOR64(lanes[21], AND64(NOT64(lanes[22]), lanes[23])) + end + end + end + + + local A5_long = 0xA5A5A5A5 * int64(2^32 + 1) -- It's impossible to use constant 0xA5A5A5A5A5A5A5A5LL because it will raise syntax error on other Lua versions + + function XORA5(long, long2) + return XOR64(long, long2 or A5_long) + end + + + -- SHA512 implementation for "LuaJIT 2.1 + FFI" branch + + function sha512_feed_128(H, _, str, offs, size) + -- offs >= 0, size >= 0, size is multiple of 128 + local W, K = common_W_FFI_int64, sha2_K_lo + for pos = offs, offs + size - 1, 128 do + for j = 0, 15 do + pos = pos + 8 + local a, b, c, d, e, f, g, h = byte(str, pos - 7, pos) -- slow, but doesn't depend on endianness + W[j] = OR64(OR(SHL(a, 24), SHL(b, 16), SHL(c, 8), d) * int64(2^32), uint32(int32(OR(SHL(e, 24), SHL(f, 16), SHL(g, 8), h)))) + end + for j = 16, 79 do + local a, b = W[j-15], W[j-2] + W[j] = XOR64(ROR64(a, 1), ROR64(a, 8), SHR64(a, 7)) + XOR64(ROR64(b, 19), ROL64(b, 3), SHR64(b, 6)) + W[j-7] + W[j-16] + end + local a, b, c, d, e, f, g, h = H[1], H[2], H[3], H[4], H[5], H[6], H[7], H[8] + for j = 0, 79, 8 do + local z = XOR64(ROR64(e, 14), ROR64(e, 18), ROL64(e, 23)) + XOR64(g, AND64(e, XOR64(f, g))) + h + K[j+1] + W[j] + h, g, f, e = g, f, e, z + d + d, c, b, a = c, b, a, XOR64(AND64(XOR64(a, b), c), AND64(a, b)) + XOR64(ROR64(a, 28), ROL64(a, 25), ROL64(a, 30)) + z + z = XOR64(ROR64(e, 14), ROR64(e, 18), ROL64(e, 23)) + XOR64(g, AND64(e, XOR64(f, g))) + h + K[j+2] + W[j+1] + h, g, f, e = g, f, e, z + d + d, c, b, a = c, b, a, XOR64(AND64(XOR64(a, b), c), AND64(a, b)) + XOR64(ROR64(a, 28), ROL64(a, 25), ROL64(a, 30)) + z + z = XOR64(ROR64(e, 14), ROR64(e, 18), ROL64(e, 23)) + XOR64(g, AND64(e, XOR64(f, g))) + h + K[j+3] + W[j+2] + h, g, f, e = g, f, e, z + d + d, c, b, a = c, b, a, XOR64(AND64(XOR64(a, b), c), AND64(a, b)) + XOR64(ROR64(a, 28), ROL64(a, 25), ROL64(a, 30)) + z + z = XOR64(ROR64(e, 14), ROR64(e, 18), ROL64(e, 23)) + XOR64(g, AND64(e, XOR64(f, g))) + h + K[j+4] + W[j+3] + h, g, f, e = g, f, e, z + d + d, c, b, a = c, b, a, XOR64(AND64(XOR64(a, b), c), AND64(a, b)) + XOR64(ROR64(a, 28), ROL64(a, 25), ROL64(a, 30)) + z + z = XOR64(ROR64(e, 14), ROR64(e, 18), ROL64(e, 23)) + XOR64(g, AND64(e, XOR64(f, g))) + h + K[j+5] + W[j+4] + h, g, f, e = g, f, e, z + d + d, c, b, a = c, b, a, XOR64(AND64(XOR64(a, b), c), AND64(a, b)) + XOR64(ROR64(a, 28), ROL64(a, 25), ROL64(a, 30)) + z + z = XOR64(ROR64(e, 14), ROR64(e, 18), ROL64(e, 23)) + XOR64(g, AND64(e, XOR64(f, g))) + h + K[j+6] + W[j+5] + h, g, f, e = g, f, e, z + d + d, c, b, a = c, b, a, XOR64(AND64(XOR64(a, b), c), AND64(a, b)) + XOR64(ROR64(a, 28), ROL64(a, 25), ROL64(a, 30)) + z + z = XOR64(ROR64(e, 14), ROR64(e, 18), ROL64(e, 23)) + XOR64(g, AND64(e, XOR64(f, g))) + h + K[j+7] + W[j+6] + h, g, f, e = g, f, e, z + d + d, c, b, a = c, b, a, XOR64(AND64(XOR64(a, b), c), AND64(a, b)) + XOR64(ROR64(a, 28), ROL64(a, 25), ROL64(a, 30)) + z + z = XOR64(ROR64(e, 14), ROR64(e, 18), ROL64(e, 23)) + XOR64(g, AND64(e, XOR64(f, g))) + h + K[j+8] + W[j+7] + h, g, f, e = g, f, e, z + d + d, c, b, a = c, b, a, XOR64(AND64(XOR64(a, b), c), AND64(a, b)) + XOR64(ROR64(a, 28), ROL64(a, 25), ROL64(a, 30)) + z + end + H[1] = a + H[1] + H[2] = b + H[2] + H[3] = c + H[3] + H[4] = d + H[4] + H[5] = e + H[5] + H[6] = f + H[6] + H[7] = g + H[7] + H[8] = h + H[8] + end + end + + else -- LuaJIT 2.0 doesn't support 64-bit bitwise operations + + local U = ffi.new("union{int64_t i64; struct{int32_t "..(ffi.abi("le") and "lo, hi" or "hi, lo")..";} i32;}[3]") + -- this array of unions is used for fast splitting int64 into int32_high and int32_low + + -- "xorrific" 64-bit functions :-) + -- int64 input is splitted into two int32 parts, some bitwise 32-bit operations are performed, finally the result is converted to int64 + -- these functions are needed because bit.* functions in LuaJIT 2.0 don't work with int64_t + + local function XORROR64_1(a) + -- return XOR64(ROR64(a, 1), ROR64(a, 8), SHR64(a, 7)) + U[0].i64 = a + local a_lo, a_hi = U[0].i32.lo, U[0].i32.hi + local t_lo = XOR(SHR(a_lo, 1), SHL(a_hi, 31), SHR(a_lo, 8), SHL(a_hi, 24), SHR(a_lo, 7), SHL(a_hi, 25)) + local t_hi = XOR(SHR(a_hi, 1), SHL(a_lo, 31), SHR(a_hi, 8), SHL(a_lo, 24), SHR(a_hi, 7)) + return t_hi * int64(2^32) + uint32(int32(t_lo)) + end + + local function XORROR64_2(b) + -- return XOR64(ROR64(b, 19), ROL64(b, 3), SHR64(b, 6)) + U[0].i64 = b + local b_lo, b_hi = U[0].i32.lo, U[0].i32.hi + local u_lo = XOR(SHR(b_lo, 19), SHL(b_hi, 13), SHL(b_lo, 3), SHR(b_hi, 29), SHR(b_lo, 6), SHL(b_hi, 26)) + local u_hi = XOR(SHR(b_hi, 19), SHL(b_lo, 13), SHL(b_hi, 3), SHR(b_lo, 29), SHR(b_hi, 6)) + return u_hi * int64(2^32) + uint32(int32(u_lo)) + end + + local function XORROR64_3(e) + -- return XOR64(ROR64(e, 14), ROR64(e, 18), ROL64(e, 23)) + U[0].i64 = e + local e_lo, e_hi = U[0].i32.lo, U[0].i32.hi + local u_lo = XOR(SHR(e_lo, 14), SHL(e_hi, 18), SHR(e_lo, 18), SHL(e_hi, 14), SHL(e_lo, 23), SHR(e_hi, 9)) + local u_hi = XOR(SHR(e_hi, 14), SHL(e_lo, 18), SHR(e_hi, 18), SHL(e_lo, 14), SHL(e_hi, 23), SHR(e_lo, 9)) + return u_hi * int64(2^32) + uint32(int32(u_lo)) + end + + local function XORROR64_6(a) + -- return XOR64(ROR64(a, 28), ROL64(a, 25), ROL64(a, 30)) + U[0].i64 = a + local b_lo, b_hi = U[0].i32.lo, U[0].i32.hi + local u_lo = XOR(SHR(b_lo, 28), SHL(b_hi, 4), SHL(b_lo, 30), SHR(b_hi, 2), SHL(b_lo, 25), SHR(b_hi, 7)) + local u_hi = XOR(SHR(b_hi, 28), SHL(b_lo, 4), SHL(b_hi, 30), SHR(b_lo, 2), SHL(b_hi, 25), SHR(b_lo, 7)) + return u_hi * int64(2^32) + uint32(int32(u_lo)) + end + + local function XORROR64_4(e, f, g) + -- return XOR64(g, AND64(e, XOR64(f, g))) + U[0].i64 = f + U[1].i64 = g + U[2].i64 = e + local f_lo, f_hi = U[0].i32.lo, U[0].i32.hi + local g_lo, g_hi = U[1].i32.lo, U[1].i32.hi + local e_lo, e_hi = U[2].i32.lo, U[2].i32.hi + local result_lo = XOR(g_lo, AND(e_lo, XOR(f_lo, g_lo))) + local result_hi = XOR(g_hi, AND(e_hi, XOR(f_hi, g_hi))) + return result_hi * int64(2^32) + uint32(int32(result_lo)) + end + + local function XORROR64_5(a, b, c) + -- return XOR64(AND64(XOR64(a, b), c), AND64(a, b)) + U[0].i64 = a + U[1].i64 = b + U[2].i64 = c + local a_lo, a_hi = U[0].i32.lo, U[0].i32.hi + local b_lo, b_hi = U[1].i32.lo, U[1].i32.hi + local c_lo, c_hi = U[2].i32.lo, U[2].i32.hi + local result_lo = XOR(AND(XOR(a_lo, b_lo), c_lo), AND(a_lo, b_lo)) + local result_hi = XOR(AND(XOR(a_hi, b_hi), c_hi), AND(a_hi, b_hi)) + return result_hi * int64(2^32) + uint32(int32(result_lo)) + end + + local function XORROR64_7(a, b, m) + -- return ROR64(XOR64(a, b), m), m = 1..31 + U[0].i64 = a + U[1].i64 = b + local a_lo, a_hi = U[0].i32.lo, U[0].i32.hi + local b_lo, b_hi = U[1].i32.lo, U[1].i32.hi + local c_lo, c_hi = XOR(a_lo, b_lo), XOR(a_hi, b_hi) + local t_lo = XOR(SHR(c_lo, m), SHL(c_hi, -m)) + local t_hi = XOR(SHR(c_hi, m), SHL(c_lo, -m)) + return t_hi * int64(2^32) + uint32(int32(t_lo)) + end + + local function XORROR64_8(a, b) + -- return ROL64(XOR64(a, b), 1) + U[0].i64 = a + U[1].i64 = b + local a_lo, a_hi = U[0].i32.lo, U[0].i32.hi + local b_lo, b_hi = U[1].i32.lo, U[1].i32.hi + local c_lo, c_hi = XOR(a_lo, b_lo), XOR(a_hi, b_hi) + local t_lo = XOR(SHL(c_lo, 1), SHR(c_hi, 31)) + local t_hi = XOR(SHL(c_hi, 1), SHR(c_lo, 31)) + return t_hi * int64(2^32) + uint32(int32(t_lo)) + end + + local function XORROR64_9(a, b) + -- return ROR64(XOR64(a, b), 32) + U[0].i64 = a + U[1].i64 = b + local a_lo, a_hi = U[0].i32.lo, U[0].i32.hi + local b_lo, b_hi = U[1].i32.lo, U[1].i32.hi + local t_hi, t_lo = XOR(a_lo, b_lo), XOR(a_hi, b_hi) + return t_hi * int64(2^32) + uint32(int32(t_lo)) + end + + local function XOR64(a, b) + -- return XOR64(a, b) + U[0].i64 = a + U[1].i64 = b + local a_lo, a_hi = U[0].i32.lo, U[0].i32.hi + local b_lo, b_hi = U[1].i32.lo, U[1].i32.hi + local t_lo, t_hi = XOR(a_lo, b_lo), XOR(a_hi, b_hi) + return t_hi * int64(2^32) + uint32(int32(t_lo)) + end + + local function XORROR64_11(a, b, c) + -- return XOR64(a, b, c) + U[0].i64 = a + U[1].i64 = b + U[2].i64 = c + local a_lo, a_hi = U[0].i32.lo, U[0].i32.hi + local b_lo, b_hi = U[1].i32.lo, U[1].i32.hi + local c_lo, c_hi = U[2].i32.lo, U[2].i32.hi + local t_lo, t_hi = XOR(a_lo, b_lo, c_lo), XOR(a_hi, b_hi, c_hi) + return t_hi * int64(2^32) + uint32(int32(t_lo)) + end + + function XORA5(long, long2) + -- return XOR64(long, long2 or 0xA5A5A5A5A5A5A5A5) + U[0].i64 = long + local lo32, hi32 = U[0].i32.lo, U[0].i32.hi + local long2_lo, long2_hi = 0xA5A5A5A5, 0xA5A5A5A5 + if long2 then + U[1].i64 = long2 + long2_lo, long2_hi = U[1].i32.lo, U[1].i32.hi + end + lo32 = XOR(lo32, long2_lo) + hi32 = XOR(hi32, long2_hi) + return hi32 * int64(2^32) + uint32(int32(lo32)) + end + + function HEX64(long) + U[0].i64 = long + return HEX(U[0].i32.hi)..HEX(U[0].i32.lo) + end + + + -- SHA512 implementation for "LuaJIT 2.0 + FFI" branch + + function sha512_feed_128(H, _, str, offs, size) + -- offs >= 0, size >= 0, size is multiple of 128 + local W, K = common_W_FFI_int64, sha2_K_lo + for pos = offs, offs + size - 1, 128 do + for j = 0, 15 do + pos = pos + 8 + local a, b, c, d, e, f, g, h = byte(str, pos - 7, pos) -- slow, but doesn't depend on endianness + W[j] = OR(SHL(a, 24), SHL(b, 16), SHL(c, 8), d) * int64(2^32) + uint32(int32(OR(SHL(e, 24), SHL(f, 16), SHL(g, 8), h))) + end + for j = 16, 79 do + W[j] = XORROR64_1(W[j-15]) + XORROR64_2(W[j-2]) + W[j-7] + W[j-16] + end + local a, b, c, d, e, f, g, h = H[1], H[2], H[3], H[4], H[5], H[6], H[7], H[8] + for j = 0, 79, 8 do + local z = XORROR64_3(e) + XORROR64_4(e, f, g) + h + K[j+1] + W[j] + h, g, f, e = g, f, e, z + d + d, c, b, a = c, b, a, XORROR64_5(a, b, c) + XORROR64_6(a) + z + z = XORROR64_3(e) + XORROR64_4(e, f, g) + h + K[j+2] + W[j+1] + h, g, f, e = g, f, e, z + d + d, c, b, a = c, b, a, XORROR64_5(a, b, c) + XORROR64_6(a) + z + z = XORROR64_3(e) + XORROR64_4(e, f, g) + h + K[j+3] + W[j+2] + h, g, f, e = g, f, e, z + d + d, c, b, a = c, b, a, XORROR64_5(a, b, c) + XORROR64_6(a) + z + z = XORROR64_3(e) + XORROR64_4(e, f, g) + h + K[j+4] + W[j+3] + h, g, f, e = g, f, e, z + d + d, c, b, a = c, b, a, XORROR64_5(a, b, c) + XORROR64_6(a) + z + z = XORROR64_3(e) + XORROR64_4(e, f, g) + h + K[j+5] + W[j+4] + h, g, f, e = g, f, e, z + d + d, c, b, a = c, b, a, XORROR64_5(a, b, c) + XORROR64_6(a) + z + z = XORROR64_3(e) + XORROR64_4(e, f, g) + h + K[j+6] + W[j+5] + h, g, f, e = g, f, e, z + d + d, c, b, a = c, b, a, XORROR64_5(a, b, c) + XORROR64_6(a) + z + z = XORROR64_3(e) + XORROR64_4(e, f, g) + h + K[j+7] + W[j+6] + h, g, f, e = g, f, e, z + d + d, c, b, a = c, b, a, XORROR64_5(a, b, c) + XORROR64_6(a) + z + z = XORROR64_3(e) + XORROR64_4(e, f, g) + h + K[j+8] + W[j+7] + h, g, f, e = g, f, e, z + d + d, c, b, a = c, b, a, XORROR64_5(a, b, c) + XORROR64_6(a) + z + end + H[1] = a + H[1] + H[2] = b + H[2] + H[3] = c + H[3] + H[4] = d + H[4] + H[5] = e + H[5] + H[6] = f + H[6] + H[7] = g + H[7] + H[8] = h + H[8] + end + end + + + -- BLAKE2b implementation for "LuaJIT 2.0 + FFI" branch + + do + local v = ffi.new("int64_t[?]", 16) + local W = common_W_blake2b + + local function G(a, b, c, d, k1, k2) + local va, vb, vc, vd = v[a], v[b], v[c], v[d] + va = W[k1] + (va + vb) + vd = XORROR64_9(vd, va) + vc = vc + vd + vb = XORROR64_7(vb, vc, 24) + va = W[k2] + (va + vb) + vd = XORROR64_7(vd, va, 16) + vc = vc + vd + vb = XORROR64_8(vb, vc) + v[a], v[b], v[c], v[d] = va, vb, vc, vd + end + + function blake2b_feed_128(H, _, str, offs, size, bytes_compressed, last_block_size, is_last_node) + -- offs >= 0, size >= 0, size is multiple of 128 + local h1, h2, h3, h4, h5, h6, h7, h8 = H[1], H[2], H[3], H[4], H[5], H[6], H[7], H[8] + for pos = offs, offs + size - 1, 128 do + if str then + for j = 1, 16 do + pos = pos + 8 + local a, b, c, d, e, f, g, h = byte(str, pos - 7, pos) + W[j] = XOR64(OR(SHL(h, 24), SHL(g, 16), SHL(f, 8), e) * int64(2^32), uint32(int32(OR(SHL(d, 24), SHL(c, 16), SHL(b, 8), a)))) + end + end + v[0x0], v[0x1], v[0x2], v[0x3], v[0x4], v[0x5], v[0x6], v[0x7] = h1, h2, h3, h4, h5, h6, h7, h8 + v[0x8], v[0x9], v[0xA], v[0xB], v[0xD], v[0xE], v[0xF] = sha2_H_lo[1], sha2_H_lo[2], sha2_H_lo[3], sha2_H_lo[4], sha2_H_lo[6], sha2_H_lo[7], sha2_H_lo[8] + bytes_compressed = bytes_compressed + (last_block_size or 128) + v[0xC] = XOR64(sha2_H_lo[5], bytes_compressed) -- t0 = low_8_bytes(bytes_compressed) + -- t1 = high_8_bytes(bytes_compressed) = 0, message length is always below 2^53 bytes + if last_block_size then -- flag f0 + v[0xE] = -1 - v[0xE] + end + if is_last_node then -- flag f1 + v[0xF] = -1 - v[0xF] + end + for j = 1, 12 do + local row = sigma[j] + G(0, 4, 8, 12, row[ 1], row[ 2]) + G(1, 5, 9, 13, row[ 3], row[ 4]) + G(2, 6, 10, 14, row[ 5], row[ 6]) + G(3, 7, 11, 15, row[ 7], row[ 8]) + G(0, 5, 10, 15, row[ 9], row[10]) + G(1, 6, 11, 12, row[11], row[12]) + G(2, 7, 8, 13, row[13], row[14]) + G(3, 4, 9, 14, row[15], row[16]) + end + h1 = XORROR64_11(h1, v[0x0], v[0x8]) + h2 = XORROR64_11(h2, v[0x1], v[0x9]) + h3 = XORROR64_11(h3, v[0x2], v[0xA]) + h4 = XORROR64_11(h4, v[0x3], v[0xB]) + h5 = XORROR64_11(h5, v[0x4], v[0xC]) + h6 = XORROR64_11(h6, v[0x5], v[0xD]) + h7 = XORROR64_11(h7, v[0x6], v[0xE]) + h8 = XORROR64_11(h8, v[0x7], v[0xF]) + end + H[1], H[2], H[3], H[4], H[5], H[6], H[7], H[8] = h1, h2, h3, h4, h5, h6, h7, h8 + return bytes_compressed + end + + end + + end + + + -- MD5 implementation for "LuaJIT with FFI" branch + + function md5_feed_64(H, str, offs, size) + -- offs >= 0, size >= 0, size is multiple of 64 + local W, K = common_W_FFI_int32, md5_K + for pos = offs, offs + size - 1, 64 do + for j = 0, 15 do + pos = pos + 4 + local a, b, c, d = byte(str, pos - 3, pos) -- slow, but doesn't depend on endianness + W[j] = OR(SHL(d, 24), SHL(c, 16), SHL(b, 8), a) + end + local a, b, c, d = H[1], H[2], H[3], H[4] + for j = 0, 15, 4 do + a, d, c, b = d, c, b, NORM(ROL(XOR(d, AND(b, XOR(c, d))) + (K[j+1] + W[j ] + a), 7) + b) + a, d, c, b = d, c, b, NORM(ROL(XOR(d, AND(b, XOR(c, d))) + (K[j+2] + W[j+1] + a), 12) + b) + a, d, c, b = d, c, b, NORM(ROL(XOR(d, AND(b, XOR(c, d))) + (K[j+3] + W[j+2] + a), 17) + b) + a, d, c, b = d, c, b, NORM(ROL(XOR(d, AND(b, XOR(c, d))) + (K[j+4] + W[j+3] + a), 22) + b) + end + for j = 16, 31, 4 do + local g = 5*j + a, d, c, b = d, c, b, NORM(ROL(XOR(c, AND(d, XOR(b, c))) + (K[j+1] + W[AND(g + 1, 15)] + a), 5) + b) + a, d, c, b = d, c, b, NORM(ROL(XOR(c, AND(d, XOR(b, c))) + (K[j+2] + W[AND(g + 6, 15)] + a), 9) + b) + a, d, c, b = d, c, b, NORM(ROL(XOR(c, AND(d, XOR(b, c))) + (K[j+3] + W[AND(g - 5, 15)] + a), 14) + b) + a, d, c, b = d, c, b, NORM(ROL(XOR(c, AND(d, XOR(b, c))) + (K[j+4] + W[AND(g , 15)] + a), 20) + b) + end + for j = 32, 47, 4 do + local g = 3*j + a, d, c, b = d, c, b, NORM(ROL(XOR(b, c, d) + (K[j+1] + W[AND(g + 5, 15)] + a), 4) + b) + a, d, c, b = d, c, b, NORM(ROL(XOR(b, c, d) + (K[j+2] + W[AND(g + 8, 15)] + a), 11) + b) + a, d, c, b = d, c, b, NORM(ROL(XOR(b, c, d) + (K[j+3] + W[AND(g - 5, 15)] + a), 16) + b) + a, d, c, b = d, c, b, NORM(ROL(XOR(b, c, d) + (K[j+4] + W[AND(g - 2, 15)] + a), 23) + b) + end + for j = 48, 63, 4 do + local g = 7*j + a, d, c, b = d, c, b, NORM(ROL(XOR(c, OR(b, NOT(d))) + (K[j+1] + W[AND(g , 15)] + a), 6) + b) + a, d, c, b = d, c, b, NORM(ROL(XOR(c, OR(b, NOT(d))) + (K[j+2] + W[AND(g + 7, 15)] + a), 10) + b) + a, d, c, b = d, c, b, NORM(ROL(XOR(c, OR(b, NOT(d))) + (K[j+3] + W[AND(g - 2, 15)] + a), 15) + b) + a, d, c, b = d, c, b, NORM(ROL(XOR(c, OR(b, NOT(d))) + (K[j+4] + W[AND(g + 5, 15)] + a), 21) + b) + end + H[1], H[2], H[3], H[4] = NORM(a + H[1]), NORM(b + H[2]), NORM(c + H[3]), NORM(d + H[4]) + end + end + + + -- SHA-1 implementation for "LuaJIT with FFI" branch + + function sha1_feed_64(H, str, offs, size) + -- offs >= 0, size >= 0, size is multiple of 64 + local W = common_W_FFI_int32 + for pos = offs, offs + size - 1, 64 do + for j = 0, 15 do + pos = pos + 4 + local a, b, c, d = byte(str, pos - 3, pos) -- slow, but doesn't depend on endianness + W[j] = OR(SHL(a, 24), SHL(b, 16), SHL(c, 8), d) + end + for j = 16, 79 do + W[j] = ROL(XOR(W[j-3], W[j-8], W[j-14], W[j-16]), 1) + end + local a, b, c, d, e = H[1], H[2], H[3], H[4], H[5] + for j = 0, 19, 5 do + e, d, c, b, a = d, c, ROR(b, 2), a, NORM(ROL(a, 5) + XOR(d, AND(b, XOR(d, c))) + (W[j] + 0x5A827999 + e)) -- constant = floor(2^30 * sqrt(2)) + e, d, c, b, a = d, c, ROR(b, 2), a, NORM(ROL(a, 5) + XOR(d, AND(b, XOR(d, c))) + (W[j+1] + 0x5A827999 + e)) + e, d, c, b, a = d, c, ROR(b, 2), a, NORM(ROL(a, 5) + XOR(d, AND(b, XOR(d, c))) + (W[j+2] + 0x5A827999 + e)) + e, d, c, b, a = d, c, ROR(b, 2), a, NORM(ROL(a, 5) + XOR(d, AND(b, XOR(d, c))) + (W[j+3] + 0x5A827999 + e)) + e, d, c, b, a = d, c, ROR(b, 2), a, NORM(ROL(a, 5) + XOR(d, AND(b, XOR(d, c))) + (W[j+4] + 0x5A827999 + e)) + end + for j = 20, 39, 5 do + e, d, c, b, a = d, c, ROR(b, 2), a, NORM(ROL(a, 5) + XOR(b, c, d) + (W[j] + 0x6ED9EBA1 + e)) -- 2^30 * sqrt(3) + e, d, c, b, a = d, c, ROR(b, 2), a, NORM(ROL(a, 5) + XOR(b, c, d) + (W[j+1] + 0x6ED9EBA1 + e)) + e, d, c, b, a = d, c, ROR(b, 2), a, NORM(ROL(a, 5) + XOR(b, c, d) + (W[j+2] + 0x6ED9EBA1 + e)) + e, d, c, b, a = d, c, ROR(b, 2), a, NORM(ROL(a, 5) + XOR(b, c, d) + (W[j+3] + 0x6ED9EBA1 + e)) + e, d, c, b, a = d, c, ROR(b, 2), a, NORM(ROL(a, 5) + XOR(b, c, d) + (W[j+4] + 0x6ED9EBA1 + e)) + end + for j = 40, 59, 5 do + e, d, c, b, a = d, c, ROR(b, 2), a, NORM(ROL(a, 5) + XOR(AND(d, XOR(b, c)), AND(b, c)) + (W[j] + 0x8F1BBCDC + e)) -- 2^30 * sqrt(5) + e, d, c, b, a = d, c, ROR(b, 2), a, NORM(ROL(a, 5) + XOR(AND(d, XOR(b, c)), AND(b, c)) + (W[j+1] + 0x8F1BBCDC + e)) + e, d, c, b, a = d, c, ROR(b, 2), a, NORM(ROL(a, 5) + XOR(AND(d, XOR(b, c)), AND(b, c)) + (W[j+2] + 0x8F1BBCDC + e)) + e, d, c, b, a = d, c, ROR(b, 2), a, NORM(ROL(a, 5) + XOR(AND(d, XOR(b, c)), AND(b, c)) + (W[j+3] + 0x8F1BBCDC + e)) + e, d, c, b, a = d, c, ROR(b, 2), a, NORM(ROL(a, 5) + XOR(AND(d, XOR(b, c)), AND(b, c)) + (W[j+4] + 0x8F1BBCDC + e)) + end + for j = 60, 79, 5 do + e, d, c, b, a = d, c, ROR(b, 2), a, NORM(ROL(a, 5) + XOR(b, c, d) + (W[j] + 0xCA62C1D6 + e)) -- 2^30 * sqrt(10) + e, d, c, b, a = d, c, ROR(b, 2), a, NORM(ROL(a, 5) + XOR(b, c, d) + (W[j+1] + 0xCA62C1D6 + e)) + e, d, c, b, a = d, c, ROR(b, 2), a, NORM(ROL(a, 5) + XOR(b, c, d) + (W[j+2] + 0xCA62C1D6 + e)) + e, d, c, b, a = d, c, ROR(b, 2), a, NORM(ROL(a, 5) + XOR(b, c, d) + (W[j+3] + 0xCA62C1D6 + e)) + e, d, c, b, a = d, c, ROR(b, 2), a, NORM(ROL(a, 5) + XOR(b, c, d) + (W[j+4] + 0xCA62C1D6 + e)) + end + H[1], H[2], H[3], H[4], H[5] = NORM(a + H[1]), NORM(b + H[2]), NORM(c + H[3]), NORM(d + H[4]), NORM(e + H[5]) + end + end + +end + + +if branch == "FFI" and not is_LuaJIT_21 or branch == "LJ" then + + if branch == "FFI" then + local arr32_t = ffi.typeof"int32_t[?]" + + function create_array_of_lanes() + return arr32_t(31) -- 25 + 5 + 1 (due to 1-based indexing) + end + + end + + + -- SHA-3 implementation for "LuaJIT 2.0 + FFI" and "LuaJIT without FFI" branches + + function keccak_feed(lanes_lo, lanes_hi, str, offs, size, block_size_in_bytes) + -- offs >= 0, size >= 0, size is multiple of block_size_in_bytes, block_size_in_bytes is positive multiple of 8 + local RC_lo, RC_hi = sha3_RC_lo, sha3_RC_hi + local qwords_qty = SHR(block_size_in_bytes, 3) + for pos = offs, offs + size - 1, block_size_in_bytes do + for j = 1, qwords_qty do + local a, b, c, d = byte(str, pos + 1, pos + 4) + lanes_lo[j] = XOR(lanes_lo[j], OR(SHL(d, 24), SHL(c, 16), SHL(b, 8), a)) + pos = pos + 8 + a, b, c, d = byte(str, pos - 3, pos) + lanes_hi[j] = XOR(lanes_hi[j], OR(SHL(d, 24), SHL(c, 16), SHL(b, 8), a)) + end + for round_idx = 1, 24 do + for j = 1, 5 do + lanes_lo[25 + j] = XOR(lanes_lo[j], lanes_lo[j + 5], lanes_lo[j + 10], lanes_lo[j + 15], lanes_lo[j + 20]) + end + for j = 1, 5 do + lanes_hi[25 + j] = XOR(lanes_hi[j], lanes_hi[j + 5], lanes_hi[j + 10], lanes_hi[j + 15], lanes_hi[j + 20]) + end + local D_lo = XOR(lanes_lo[26], SHL(lanes_lo[28], 1), SHR(lanes_hi[28], 31)) + local D_hi = XOR(lanes_hi[26], SHL(lanes_hi[28], 1), SHR(lanes_lo[28], 31)) + lanes_lo[2], lanes_hi[2], lanes_lo[7], lanes_hi[7], lanes_lo[12], lanes_hi[12], lanes_lo[17], lanes_hi[17] = XOR(SHR(XOR(D_lo, lanes_lo[7]), 20), SHL(XOR(D_hi, lanes_hi[7]), 12)), XOR(SHR(XOR(D_hi, lanes_hi[7]), 20), SHL(XOR(D_lo, lanes_lo[7]), 12)), XOR(SHR(XOR(D_lo, lanes_lo[17]), 19), SHL(XOR(D_hi, lanes_hi[17]), 13)), XOR(SHR(XOR(D_hi, lanes_hi[17]), 19), SHL(XOR(D_lo, lanes_lo[17]), 13)), XOR(SHL(XOR(D_lo, lanes_lo[2]), 1), SHR(XOR(D_hi, lanes_hi[2]), 31)), XOR(SHL(XOR(D_hi, lanes_hi[2]), 1), SHR(XOR(D_lo, lanes_lo[2]), 31)), XOR(SHL(XOR(D_lo, lanes_lo[12]), 10), SHR(XOR(D_hi, lanes_hi[12]), 22)), XOR(SHL(XOR(D_hi, lanes_hi[12]), 10), SHR(XOR(D_lo, lanes_lo[12]), 22)) + local L, H = XOR(D_lo, lanes_lo[22]), XOR(D_hi, lanes_hi[22]) + lanes_lo[22], lanes_hi[22] = XOR(SHL(L, 2), SHR(H, 30)), XOR(SHL(H, 2), SHR(L, 30)) + D_lo = XOR(lanes_lo[27], SHL(lanes_lo[29], 1), SHR(lanes_hi[29], 31)) + D_hi = XOR(lanes_hi[27], SHL(lanes_hi[29], 1), SHR(lanes_lo[29], 31)) + lanes_lo[3], lanes_hi[3], lanes_lo[8], lanes_hi[8], lanes_lo[13], lanes_hi[13], lanes_lo[23], lanes_hi[23] = XOR(SHR(XOR(D_lo, lanes_lo[13]), 21), SHL(XOR(D_hi, lanes_hi[13]), 11)), XOR(SHR(XOR(D_hi, lanes_hi[13]), 21), SHL(XOR(D_lo, lanes_lo[13]), 11)), XOR(SHR(XOR(D_lo, lanes_lo[23]), 3), SHL(XOR(D_hi, lanes_hi[23]), 29)), XOR(SHR(XOR(D_hi, lanes_hi[23]), 3), SHL(XOR(D_lo, lanes_lo[23]), 29)), XOR(SHL(XOR(D_lo, lanes_lo[8]), 6), SHR(XOR(D_hi, lanes_hi[8]), 26)), XOR(SHL(XOR(D_hi, lanes_hi[8]), 6), SHR(XOR(D_lo, lanes_lo[8]), 26)), XOR(SHR(XOR(D_lo, lanes_lo[3]), 2), SHL(XOR(D_hi, lanes_hi[3]), 30)), XOR(SHR(XOR(D_hi, lanes_hi[3]), 2), SHL(XOR(D_lo, lanes_lo[3]), 30)) + L, H = XOR(D_lo, lanes_lo[18]), XOR(D_hi, lanes_hi[18]) + lanes_lo[18], lanes_hi[18] = XOR(SHL(L, 15), SHR(H, 17)), XOR(SHL(H, 15), SHR(L, 17)) + D_lo = XOR(lanes_lo[28], SHL(lanes_lo[30], 1), SHR(lanes_hi[30], 31)) + D_hi = XOR(lanes_hi[28], SHL(lanes_hi[30], 1), SHR(lanes_lo[30], 31)) + lanes_lo[4], lanes_hi[4], lanes_lo[9], lanes_hi[9], lanes_lo[19], lanes_hi[19], lanes_lo[24], lanes_hi[24] = XOR(SHL(XOR(D_lo, lanes_lo[19]), 21), SHR(XOR(D_hi, lanes_hi[19]), 11)), XOR(SHL(XOR(D_hi, lanes_hi[19]), 21), SHR(XOR(D_lo, lanes_lo[19]), 11)), XOR(SHL(XOR(D_lo, lanes_lo[4]), 28), SHR(XOR(D_hi, lanes_hi[4]), 4)), XOR(SHL(XOR(D_hi, lanes_hi[4]), 28), SHR(XOR(D_lo, lanes_lo[4]), 4)), XOR(SHR(XOR(D_lo, lanes_lo[24]), 8), SHL(XOR(D_hi, lanes_hi[24]), 24)), XOR(SHR(XOR(D_hi, lanes_hi[24]), 8), SHL(XOR(D_lo, lanes_lo[24]), 24)), XOR(SHR(XOR(D_lo, lanes_lo[9]), 9), SHL(XOR(D_hi, lanes_hi[9]), 23)), XOR(SHR(XOR(D_hi, lanes_hi[9]), 9), SHL(XOR(D_lo, lanes_lo[9]), 23)) + L, H = XOR(D_lo, lanes_lo[14]), XOR(D_hi, lanes_hi[14]) + lanes_lo[14], lanes_hi[14] = XOR(SHL(L, 25), SHR(H, 7)), XOR(SHL(H, 25), SHR(L, 7)) + D_lo = XOR(lanes_lo[29], SHL(lanes_lo[26], 1), SHR(lanes_hi[26], 31)) + D_hi = XOR(lanes_hi[29], SHL(lanes_hi[26], 1), SHR(lanes_lo[26], 31)) + lanes_lo[5], lanes_hi[5], lanes_lo[15], lanes_hi[15], lanes_lo[20], lanes_hi[20], lanes_lo[25], lanes_hi[25] = XOR(SHL(XOR(D_lo, lanes_lo[25]), 14), SHR(XOR(D_hi, lanes_hi[25]), 18)), XOR(SHL(XOR(D_hi, lanes_hi[25]), 14), SHR(XOR(D_lo, lanes_lo[25]), 18)), XOR(SHL(XOR(D_lo, lanes_lo[20]), 8), SHR(XOR(D_hi, lanes_hi[20]), 24)), XOR(SHL(XOR(D_hi, lanes_hi[20]), 8), SHR(XOR(D_lo, lanes_lo[20]), 24)), XOR(SHL(XOR(D_lo, lanes_lo[5]), 27), SHR(XOR(D_hi, lanes_hi[5]), 5)), XOR(SHL(XOR(D_hi, lanes_hi[5]), 27), SHR(XOR(D_lo, lanes_lo[5]), 5)), XOR(SHR(XOR(D_lo, lanes_lo[15]), 25), SHL(XOR(D_hi, lanes_hi[15]), 7)), XOR(SHR(XOR(D_hi, lanes_hi[15]), 25), SHL(XOR(D_lo, lanes_lo[15]), 7)) + L, H = XOR(D_lo, lanes_lo[10]), XOR(D_hi, lanes_hi[10]) + lanes_lo[10], lanes_hi[10] = XOR(SHL(L, 20), SHR(H, 12)), XOR(SHL(H, 20), SHR(L, 12)) + D_lo = XOR(lanes_lo[30], SHL(lanes_lo[27], 1), SHR(lanes_hi[27], 31)) + D_hi = XOR(lanes_hi[30], SHL(lanes_hi[27], 1), SHR(lanes_lo[27], 31)) + lanes_lo[6], lanes_hi[6], lanes_lo[11], lanes_hi[11], lanes_lo[16], lanes_hi[16], lanes_lo[21], lanes_hi[21] = XOR(SHL(XOR(D_lo, lanes_lo[11]), 3), SHR(XOR(D_hi, lanes_hi[11]), 29)), XOR(SHL(XOR(D_hi, lanes_hi[11]), 3), SHR(XOR(D_lo, lanes_lo[11]), 29)), XOR(SHL(XOR(D_lo, lanes_lo[21]), 18), SHR(XOR(D_hi, lanes_hi[21]), 14)), XOR(SHL(XOR(D_hi, lanes_hi[21]), 18), SHR(XOR(D_lo, lanes_lo[21]), 14)), XOR(SHR(XOR(D_lo, lanes_lo[6]), 28), SHL(XOR(D_hi, lanes_hi[6]), 4)), XOR(SHR(XOR(D_hi, lanes_hi[6]), 28), SHL(XOR(D_lo, lanes_lo[6]), 4)), XOR(SHR(XOR(D_lo, lanes_lo[16]), 23), SHL(XOR(D_hi, lanes_hi[16]), 9)), XOR(SHR(XOR(D_hi, lanes_hi[16]), 23), SHL(XOR(D_lo, lanes_lo[16]), 9)) + lanes_lo[1], lanes_hi[1] = XOR(D_lo, lanes_lo[1]), XOR(D_hi, lanes_hi[1]) + lanes_lo[1], lanes_lo[2], lanes_lo[3], lanes_lo[4], lanes_lo[5] = XOR(lanes_lo[1], AND(NOT(lanes_lo[2]), lanes_lo[3]), RC_lo[round_idx]), XOR(lanes_lo[2], AND(NOT(lanes_lo[3]), lanes_lo[4])), XOR(lanes_lo[3], AND(NOT(lanes_lo[4]), lanes_lo[5])), XOR(lanes_lo[4], AND(NOT(lanes_lo[5]), lanes_lo[1])), XOR(lanes_lo[5], AND(NOT(lanes_lo[1]), lanes_lo[2])) + lanes_lo[6], lanes_lo[7], lanes_lo[8], lanes_lo[9], lanes_lo[10] = XOR(lanes_lo[9], AND(NOT(lanes_lo[10]), lanes_lo[6])), XOR(lanes_lo[10], AND(NOT(lanes_lo[6]), lanes_lo[7])), XOR(lanes_lo[6], AND(NOT(lanes_lo[7]), lanes_lo[8])), XOR(lanes_lo[7], AND(NOT(lanes_lo[8]), lanes_lo[9])), XOR(lanes_lo[8], AND(NOT(lanes_lo[9]), lanes_lo[10])) + lanes_lo[11], lanes_lo[12], lanes_lo[13], lanes_lo[14], lanes_lo[15] = XOR(lanes_lo[12], AND(NOT(lanes_lo[13]), lanes_lo[14])), XOR(lanes_lo[13], AND(NOT(lanes_lo[14]), lanes_lo[15])), XOR(lanes_lo[14], AND(NOT(lanes_lo[15]), lanes_lo[11])), XOR(lanes_lo[15], AND(NOT(lanes_lo[11]), lanes_lo[12])), XOR(lanes_lo[11], AND(NOT(lanes_lo[12]), lanes_lo[13])) + lanes_lo[16], lanes_lo[17], lanes_lo[18], lanes_lo[19], lanes_lo[20] = XOR(lanes_lo[20], AND(NOT(lanes_lo[16]), lanes_lo[17])), XOR(lanes_lo[16], AND(NOT(lanes_lo[17]), lanes_lo[18])), XOR(lanes_lo[17], AND(NOT(lanes_lo[18]), lanes_lo[19])), XOR(lanes_lo[18], AND(NOT(lanes_lo[19]), lanes_lo[20])), XOR(lanes_lo[19], AND(NOT(lanes_lo[20]), lanes_lo[16])) + lanes_lo[21], lanes_lo[22], lanes_lo[23], lanes_lo[24], lanes_lo[25] = XOR(lanes_lo[23], AND(NOT(lanes_lo[24]), lanes_lo[25])), XOR(lanes_lo[24], AND(NOT(lanes_lo[25]), lanes_lo[21])), XOR(lanes_lo[25], AND(NOT(lanes_lo[21]), lanes_lo[22])), XOR(lanes_lo[21], AND(NOT(lanes_lo[22]), lanes_lo[23])), XOR(lanes_lo[22], AND(NOT(lanes_lo[23]), lanes_lo[24])) + lanes_hi[1], lanes_hi[2], lanes_hi[3], lanes_hi[4], lanes_hi[5] = XOR(lanes_hi[1], AND(NOT(lanes_hi[2]), lanes_hi[3]), RC_hi[round_idx]), XOR(lanes_hi[2], AND(NOT(lanes_hi[3]), lanes_hi[4])), XOR(lanes_hi[3], AND(NOT(lanes_hi[4]), lanes_hi[5])), XOR(lanes_hi[4], AND(NOT(lanes_hi[5]), lanes_hi[1])), XOR(lanes_hi[5], AND(NOT(lanes_hi[1]), lanes_hi[2])) + lanes_hi[6], lanes_hi[7], lanes_hi[8], lanes_hi[9], lanes_hi[10] = XOR(lanes_hi[9], AND(NOT(lanes_hi[10]), lanes_hi[6])), XOR(lanes_hi[10], AND(NOT(lanes_hi[6]), lanes_hi[7])), XOR(lanes_hi[6], AND(NOT(lanes_hi[7]), lanes_hi[8])), XOR(lanes_hi[7], AND(NOT(lanes_hi[8]), lanes_hi[9])), XOR(lanes_hi[8], AND(NOT(lanes_hi[9]), lanes_hi[10])) + lanes_hi[11], lanes_hi[12], lanes_hi[13], lanes_hi[14], lanes_hi[15] = XOR(lanes_hi[12], AND(NOT(lanes_hi[13]), lanes_hi[14])), XOR(lanes_hi[13], AND(NOT(lanes_hi[14]), lanes_hi[15])), XOR(lanes_hi[14], AND(NOT(lanes_hi[15]), lanes_hi[11])), XOR(lanes_hi[15], AND(NOT(lanes_hi[11]), lanes_hi[12])), XOR(lanes_hi[11], AND(NOT(lanes_hi[12]), lanes_hi[13])) + lanes_hi[16], lanes_hi[17], lanes_hi[18], lanes_hi[19], lanes_hi[20] = XOR(lanes_hi[20], AND(NOT(lanes_hi[16]), lanes_hi[17])), XOR(lanes_hi[16], AND(NOT(lanes_hi[17]), lanes_hi[18])), XOR(lanes_hi[17], AND(NOT(lanes_hi[18]), lanes_hi[19])), XOR(lanes_hi[18], AND(NOT(lanes_hi[19]), lanes_hi[20])), XOR(lanes_hi[19], AND(NOT(lanes_hi[20]), lanes_hi[16])) + lanes_hi[21], lanes_hi[22], lanes_hi[23], lanes_hi[24], lanes_hi[25] = XOR(lanes_hi[23], AND(NOT(lanes_hi[24]), lanes_hi[25])), XOR(lanes_hi[24], AND(NOT(lanes_hi[25]), lanes_hi[21])), XOR(lanes_hi[25], AND(NOT(lanes_hi[21]), lanes_hi[22])), XOR(lanes_hi[21], AND(NOT(lanes_hi[22]), lanes_hi[23])), XOR(lanes_hi[22], AND(NOT(lanes_hi[23]), lanes_hi[24])) + end + end + end + +end + + +if branch == "LJ" then + + + -- SHA256 implementation for "LuaJIT without FFI" branch + + function sha256_feed_64(H, str, offs, size) + -- offs >= 0, size >= 0, size is multiple of 64 + local W, K = common_W, sha2_K_hi + for pos = offs, offs + size - 1, 64 do + for j = 1, 16 do + pos = pos + 4 + local a, b, c, d = byte(str, pos - 3, pos) + W[j] = OR(SHL(a, 24), SHL(b, 16), SHL(c, 8), d) + end + for j = 17, 64 do + local a, b = W[j-15], W[j-2] + W[j] = NORM( NORM( XOR(ROR(a, 7), ROL(a, 14), SHR(a, 3)) + XOR(ROL(b, 15), ROL(b, 13), SHR(b, 10)) ) + NORM( W[j-7] + W[j-16] ) ) + end + local a, b, c, d, e, f, g, h = H[1], H[2], H[3], H[4], H[5], H[6], H[7], H[8] + for j = 1, 64, 8 do -- Thanks to Peter Cawley for this workaround (unroll the loop to avoid "PHI shuffling too complex" due to PHIs overlap) + local z = NORM( XOR(ROR(e, 6), ROR(e, 11), ROL(e, 7)) + XOR(g, AND(e, XOR(f, g))) + (K[j] + W[j] + h) ) + h, g, f, e = g, f, e, NORM(d + z) + d, c, b, a = c, b, a, NORM( XOR(AND(a, XOR(b, c)), AND(b, c)) + XOR(ROR(a, 2), ROR(a, 13), ROL(a, 10)) + z ) + z = NORM( XOR(ROR(e, 6), ROR(e, 11), ROL(e, 7)) + XOR(g, AND(e, XOR(f, g))) + (K[j+1] + W[j+1] + h) ) + h, g, f, e = g, f, e, NORM(d + z) + d, c, b, a = c, b, a, NORM( XOR(AND(a, XOR(b, c)), AND(b, c)) + XOR(ROR(a, 2), ROR(a, 13), ROL(a, 10)) + z ) + z = NORM( XOR(ROR(e, 6), ROR(e, 11), ROL(e, 7)) + XOR(g, AND(e, XOR(f, g))) + (K[j+2] + W[j+2] + h) ) + h, g, f, e = g, f, e, NORM(d + z) + d, c, b, a = c, b, a, NORM( XOR(AND(a, XOR(b, c)), AND(b, c)) + XOR(ROR(a, 2), ROR(a, 13), ROL(a, 10)) + z ) + z = NORM( XOR(ROR(e, 6), ROR(e, 11), ROL(e, 7)) + XOR(g, AND(e, XOR(f, g))) + (K[j+3] + W[j+3] + h) ) + h, g, f, e = g, f, e, NORM(d + z) + d, c, b, a = c, b, a, NORM( XOR(AND(a, XOR(b, c)), AND(b, c)) + XOR(ROR(a, 2), ROR(a, 13), ROL(a, 10)) + z ) + z = NORM( XOR(ROR(e, 6), ROR(e, 11), ROL(e, 7)) + XOR(g, AND(e, XOR(f, g))) + (K[j+4] + W[j+4] + h) ) + h, g, f, e = g, f, e, NORM(d + z) + d, c, b, a = c, b, a, NORM( XOR(AND(a, XOR(b, c)), AND(b, c)) + XOR(ROR(a, 2), ROR(a, 13), ROL(a, 10)) + z ) + z = NORM( XOR(ROR(e, 6), ROR(e, 11), ROL(e, 7)) + XOR(g, AND(e, XOR(f, g))) + (K[j+5] + W[j+5] + h) ) + h, g, f, e = g, f, e, NORM(d + z) + d, c, b, a = c, b, a, NORM( XOR(AND(a, XOR(b, c)), AND(b, c)) + XOR(ROR(a, 2), ROR(a, 13), ROL(a, 10)) + z ) + z = NORM( XOR(ROR(e, 6), ROR(e, 11), ROL(e, 7)) + XOR(g, AND(e, XOR(f, g))) + (K[j+6] + W[j+6] + h) ) + h, g, f, e = g, f, e, NORM(d + z) + d, c, b, a = c, b, a, NORM( XOR(AND(a, XOR(b, c)), AND(b, c)) + XOR(ROR(a, 2), ROR(a, 13), ROL(a, 10)) + z ) + z = NORM( XOR(ROR(e, 6), ROR(e, 11), ROL(e, 7)) + XOR(g, AND(e, XOR(f, g))) + (K[j+7] + W[j+7] + h) ) + h, g, f, e = g, f, e, NORM(d + z) + d, c, b, a = c, b, a, NORM( XOR(AND(a, XOR(b, c)), AND(b, c)) + XOR(ROR(a, 2), ROR(a, 13), ROL(a, 10)) + z ) + end + H[1], H[2], H[3], H[4] = NORM(a + H[1]), NORM(b + H[2]), NORM(c + H[3]), NORM(d + H[4]) + H[5], H[6], H[7], H[8] = NORM(e + H[5]), NORM(f + H[6]), NORM(g + H[7]), NORM(h + H[8]) + end + end + + local function ADD64_4(a_lo, a_hi, b_lo, b_hi, c_lo, c_hi, d_lo, d_hi) + local sum_lo = a_lo % 2^32 + b_lo % 2^32 + c_lo % 2^32 + d_lo % 2^32 + local sum_hi = a_hi + b_hi + c_hi + d_hi + local result_lo = NORM( sum_lo ) + local result_hi = NORM( sum_hi + floor(sum_lo / 2^32) ) + return result_lo, result_hi + end + + if LuaJIT_arch == "x86" then -- Special trick is required to avoid "PHI shuffling too complex" on x86 platform + + + -- SHA512 implementation for "LuaJIT x86 without FFI" branch + + function sha512_feed_128(H_lo, H_hi, str, offs, size) + -- offs >= 0, size >= 0, size is multiple of 128 + -- W1_hi, W1_lo, W2_hi, W2_lo, ... Wk_hi = W[2*k-1], Wk_lo = W[2*k] + local W, K_lo, K_hi = common_W, sha2_K_lo, sha2_K_hi + for pos = offs, offs + size - 1, 128 do + for j = 1, 16*2 do + pos = pos + 4 + local a, b, c, d = byte(str, pos - 3, pos) + W[j] = OR(SHL(a, 24), SHL(b, 16), SHL(c, 8), d) + end + for jj = 17*2, 80*2, 2 do + local a_lo, a_hi = W[jj-30], W[jj-31] + local t_lo = XOR(OR(SHR(a_lo, 1), SHL(a_hi, 31)), OR(SHR(a_lo, 8), SHL(a_hi, 24)), OR(SHR(a_lo, 7), SHL(a_hi, 25))) + local t_hi = XOR(OR(SHR(a_hi, 1), SHL(a_lo, 31)), OR(SHR(a_hi, 8), SHL(a_lo, 24)), SHR(a_hi, 7)) + local b_lo, b_hi = W[jj-4], W[jj-5] + local u_lo = XOR(OR(SHR(b_lo, 19), SHL(b_hi, 13)), OR(SHL(b_lo, 3), SHR(b_hi, 29)), OR(SHR(b_lo, 6), SHL(b_hi, 26))) + local u_hi = XOR(OR(SHR(b_hi, 19), SHL(b_lo, 13)), OR(SHL(b_hi, 3), SHR(b_lo, 29)), SHR(b_hi, 6)) + W[jj], W[jj-1] = ADD64_4(t_lo, t_hi, u_lo, u_hi, W[jj-14], W[jj-15], W[jj-32], W[jj-33]) + end + local a_lo, b_lo, c_lo, d_lo, e_lo, f_lo, g_lo, h_lo = H_lo[1], H_lo[2], H_lo[3], H_lo[4], H_lo[5], H_lo[6], H_lo[7], H_lo[8] + local a_hi, b_hi, c_hi, d_hi, e_hi, f_hi, g_hi, h_hi = H_hi[1], H_hi[2], H_hi[3], H_hi[4], H_hi[5], H_hi[6], H_hi[7], H_hi[8] + local zero = 0 + for j = 1, 80 do + local t_lo = XOR(g_lo, AND(e_lo, XOR(f_lo, g_lo))) + local t_hi = XOR(g_hi, AND(e_hi, XOR(f_hi, g_hi))) + local u_lo = XOR(OR(SHR(e_lo, 14), SHL(e_hi, 18)), OR(SHR(e_lo, 18), SHL(e_hi, 14)), OR(SHL(e_lo, 23), SHR(e_hi, 9))) + local u_hi = XOR(OR(SHR(e_hi, 14), SHL(e_lo, 18)), OR(SHR(e_hi, 18), SHL(e_lo, 14)), OR(SHL(e_hi, 23), SHR(e_lo, 9))) + local sum_lo = u_lo % 2^32 + t_lo % 2^32 + h_lo % 2^32 + K_lo[j] + W[2*j] % 2^32 + local z_lo, z_hi = NORM( sum_lo ), NORM( u_hi + t_hi + h_hi + K_hi[j] + W[2*j-1] + floor(sum_lo / 2^32) ) + zero = zero + zero -- this thick is needed to avoid "PHI shuffling too complex" due to PHIs overlap + h_lo, h_hi, g_lo, g_hi, f_lo, f_hi = OR(zero, g_lo), OR(zero, g_hi), OR(zero, f_lo), OR(zero, f_hi), OR(zero, e_lo), OR(zero, e_hi) + local sum_lo = z_lo % 2^32 + d_lo % 2^32 + e_lo, e_hi = NORM( sum_lo ), NORM( z_hi + d_hi + floor(sum_lo / 2^32) ) + d_lo, d_hi, c_lo, c_hi, b_lo, b_hi = OR(zero, c_lo), OR(zero, c_hi), OR(zero, b_lo), OR(zero, b_hi), OR(zero, a_lo), OR(zero, a_hi) + u_lo = XOR(OR(SHR(b_lo, 28), SHL(b_hi, 4)), OR(SHL(b_lo, 30), SHR(b_hi, 2)), OR(SHL(b_lo, 25), SHR(b_hi, 7))) + u_hi = XOR(OR(SHR(b_hi, 28), SHL(b_lo, 4)), OR(SHL(b_hi, 30), SHR(b_lo, 2)), OR(SHL(b_hi, 25), SHR(b_lo, 7))) + t_lo = OR(AND(d_lo, c_lo), AND(b_lo, XOR(d_lo, c_lo))) + t_hi = OR(AND(d_hi, c_hi), AND(b_hi, XOR(d_hi, c_hi))) + local sum_lo = z_lo % 2^32 + t_lo % 2^32 + u_lo % 2^32 + a_lo, a_hi = NORM( sum_lo ), NORM( z_hi + t_hi + u_hi + floor(sum_lo / 2^32) ) + end + H_lo[1], H_hi[1] = ADD64_4(H_lo[1], H_hi[1], a_lo, a_hi, 0, 0, 0, 0) + H_lo[2], H_hi[2] = ADD64_4(H_lo[2], H_hi[2], b_lo, b_hi, 0, 0, 0, 0) + H_lo[3], H_hi[3] = ADD64_4(H_lo[3], H_hi[3], c_lo, c_hi, 0, 0, 0, 0) + H_lo[4], H_hi[4] = ADD64_4(H_lo[4], H_hi[4], d_lo, d_hi, 0, 0, 0, 0) + H_lo[5], H_hi[5] = ADD64_4(H_lo[5], H_hi[5], e_lo, e_hi, 0, 0, 0, 0) + H_lo[6], H_hi[6] = ADD64_4(H_lo[6], H_hi[6], f_lo, f_hi, 0, 0, 0, 0) + H_lo[7], H_hi[7] = ADD64_4(H_lo[7], H_hi[7], g_lo, g_hi, 0, 0, 0, 0) + H_lo[8], H_hi[8] = ADD64_4(H_lo[8], H_hi[8], h_lo, h_hi, 0, 0, 0, 0) + end + end + + else -- all platforms except x86 + + + -- SHA512 implementation for "LuaJIT non-x86 without FFI" branch + + function sha512_feed_128(H_lo, H_hi, str, offs, size) + -- offs >= 0, size >= 0, size is multiple of 128 + -- W1_hi, W1_lo, W2_hi, W2_lo, ... Wk_hi = W[2*k-1], Wk_lo = W[2*k] + local W, K_lo, K_hi = common_W, sha2_K_lo, sha2_K_hi + for pos = offs, offs + size - 1, 128 do + for j = 1, 16*2 do + pos = pos + 4 + local a, b, c, d = byte(str, pos - 3, pos) + W[j] = OR(SHL(a, 24), SHL(b, 16), SHL(c, 8), d) + end + for jj = 17*2, 80*2, 2 do + local a_lo, a_hi = W[jj-30], W[jj-31] + local t_lo = XOR(OR(SHR(a_lo, 1), SHL(a_hi, 31)), OR(SHR(a_lo, 8), SHL(a_hi, 24)), OR(SHR(a_lo, 7), SHL(a_hi, 25))) + local t_hi = XOR(OR(SHR(a_hi, 1), SHL(a_lo, 31)), OR(SHR(a_hi, 8), SHL(a_lo, 24)), SHR(a_hi, 7)) + local b_lo, b_hi = W[jj-4], W[jj-5] + local u_lo = XOR(OR(SHR(b_lo, 19), SHL(b_hi, 13)), OR(SHL(b_lo, 3), SHR(b_hi, 29)), OR(SHR(b_lo, 6), SHL(b_hi, 26))) + local u_hi = XOR(OR(SHR(b_hi, 19), SHL(b_lo, 13)), OR(SHL(b_hi, 3), SHR(b_lo, 29)), SHR(b_hi, 6)) + W[jj], W[jj-1] = ADD64_4(t_lo, t_hi, u_lo, u_hi, W[jj-14], W[jj-15], W[jj-32], W[jj-33]) + end + local a_lo, b_lo, c_lo, d_lo, e_lo, f_lo, g_lo, h_lo = H_lo[1], H_lo[2], H_lo[3], H_lo[4], H_lo[5], H_lo[6], H_lo[7], H_lo[8] + local a_hi, b_hi, c_hi, d_hi, e_hi, f_hi, g_hi, h_hi = H_hi[1], H_hi[2], H_hi[3], H_hi[4], H_hi[5], H_hi[6], H_hi[7], H_hi[8] + for j = 1, 80 do + local t_lo = XOR(g_lo, AND(e_lo, XOR(f_lo, g_lo))) + local t_hi = XOR(g_hi, AND(e_hi, XOR(f_hi, g_hi))) + local u_lo = XOR(OR(SHR(e_lo, 14), SHL(e_hi, 18)), OR(SHR(e_lo, 18), SHL(e_hi, 14)), OR(SHL(e_lo, 23), SHR(e_hi, 9))) + local u_hi = XOR(OR(SHR(e_hi, 14), SHL(e_lo, 18)), OR(SHR(e_hi, 18), SHL(e_lo, 14)), OR(SHL(e_hi, 23), SHR(e_lo, 9))) + local sum_lo = u_lo % 2^32 + t_lo % 2^32 + h_lo % 2^32 + K_lo[j] + W[2*j] % 2^32 + local z_lo, z_hi = NORM( sum_lo ), NORM( u_hi + t_hi + h_hi + K_hi[j] + W[2*j-1] + floor(sum_lo / 2^32) ) + h_lo, h_hi, g_lo, g_hi, f_lo, f_hi = g_lo, g_hi, f_lo, f_hi, e_lo, e_hi + local sum_lo = z_lo % 2^32 + d_lo % 2^32 + e_lo, e_hi = NORM( sum_lo ), NORM( z_hi + d_hi + floor(sum_lo / 2^32) ) + d_lo, d_hi, c_lo, c_hi, b_lo, b_hi = c_lo, c_hi, b_lo, b_hi, a_lo, a_hi + u_lo = XOR(OR(SHR(b_lo, 28), SHL(b_hi, 4)), OR(SHL(b_lo, 30), SHR(b_hi, 2)), OR(SHL(b_lo, 25), SHR(b_hi, 7))) + u_hi = XOR(OR(SHR(b_hi, 28), SHL(b_lo, 4)), OR(SHL(b_hi, 30), SHR(b_lo, 2)), OR(SHL(b_hi, 25), SHR(b_lo, 7))) + t_lo = OR(AND(d_lo, c_lo), AND(b_lo, XOR(d_lo, c_lo))) + t_hi = OR(AND(d_hi, c_hi), AND(b_hi, XOR(d_hi, c_hi))) + local sum_lo = z_lo % 2^32 + u_lo % 2^32 + t_lo % 2^32 + a_lo, a_hi = NORM( sum_lo ), NORM( z_hi + u_hi + t_hi + floor(sum_lo / 2^32) ) + end + H_lo[1], H_hi[1] = ADD64_4(H_lo[1], H_hi[1], a_lo, a_hi, 0, 0, 0, 0) + H_lo[2], H_hi[2] = ADD64_4(H_lo[2], H_hi[2], b_lo, b_hi, 0, 0, 0, 0) + H_lo[3], H_hi[3] = ADD64_4(H_lo[3], H_hi[3], c_lo, c_hi, 0, 0, 0, 0) + H_lo[4], H_hi[4] = ADD64_4(H_lo[4], H_hi[4], d_lo, d_hi, 0, 0, 0, 0) + H_lo[5], H_hi[5] = ADD64_4(H_lo[5], H_hi[5], e_lo, e_hi, 0, 0, 0, 0) + H_lo[6], H_hi[6] = ADD64_4(H_lo[6], H_hi[6], f_lo, f_hi, 0, 0, 0, 0) + H_lo[7], H_hi[7] = ADD64_4(H_lo[7], H_hi[7], g_lo, g_hi, 0, 0, 0, 0) + H_lo[8], H_hi[8] = ADD64_4(H_lo[8], H_hi[8], h_lo, h_hi, 0, 0, 0, 0) + end + end + + end + + + -- MD5 implementation for "LuaJIT without FFI" branch + + function md5_feed_64(H, str, offs, size) + -- offs >= 0, size >= 0, size is multiple of 64 + local W, K = common_W, md5_K + for pos = offs, offs + size - 1, 64 do + for j = 1, 16 do + pos = pos + 4 + local a, b, c, d = byte(str, pos - 3, pos) + W[j] = OR(SHL(d, 24), SHL(c, 16), SHL(b, 8), a) + end + local a, b, c, d = H[1], H[2], H[3], H[4] + for j = 1, 16, 4 do + a, d, c, b = d, c, b, NORM(ROL(XOR(d, AND(b, XOR(c, d))) + (K[j ] + W[j ] + a), 7) + b) + a, d, c, b = d, c, b, NORM(ROL(XOR(d, AND(b, XOR(c, d))) + (K[j+1] + W[j+1] + a), 12) + b) + a, d, c, b = d, c, b, NORM(ROL(XOR(d, AND(b, XOR(c, d))) + (K[j+2] + W[j+2] + a), 17) + b) + a, d, c, b = d, c, b, NORM(ROL(XOR(d, AND(b, XOR(c, d))) + (K[j+3] + W[j+3] + a), 22) + b) + end + for j = 17, 32, 4 do + local g = 5*j-4 + a, d, c, b = d, c, b, NORM(ROL(XOR(c, AND(d, XOR(b, c))) + (K[j ] + W[AND(g , 15) + 1] + a), 5) + b) + a, d, c, b = d, c, b, NORM(ROL(XOR(c, AND(d, XOR(b, c))) + (K[j+1] + W[AND(g + 5, 15) + 1] + a), 9) + b) + a, d, c, b = d, c, b, NORM(ROL(XOR(c, AND(d, XOR(b, c))) + (K[j+2] + W[AND(g + 10, 15) + 1] + a), 14) + b) + a, d, c, b = d, c, b, NORM(ROL(XOR(c, AND(d, XOR(b, c))) + (K[j+3] + W[AND(g - 1, 15) + 1] + a), 20) + b) + end + for j = 33, 48, 4 do + local g = 3*j+2 + a, d, c, b = d, c, b, NORM(ROL(XOR(b, c, d) + (K[j ] + W[AND(g , 15) + 1] + a), 4) + b) + a, d, c, b = d, c, b, NORM(ROL(XOR(b, c, d) + (K[j+1] + W[AND(g + 3, 15) + 1] + a), 11) + b) + a, d, c, b = d, c, b, NORM(ROL(XOR(b, c, d) + (K[j+2] + W[AND(g + 6, 15) + 1] + a), 16) + b) + a, d, c, b = d, c, b, NORM(ROL(XOR(b, c, d) + (K[j+3] + W[AND(g - 7, 15) + 1] + a), 23) + b) + end + for j = 49, 64, 4 do + local g = j*7 + a, d, c, b = d, c, b, NORM(ROL(XOR(c, OR(b, NOT(d))) + (K[j ] + W[AND(g - 7, 15) + 1] + a), 6) + b) + a, d, c, b = d, c, b, NORM(ROL(XOR(c, OR(b, NOT(d))) + (K[j+1] + W[AND(g , 15) + 1] + a), 10) + b) + a, d, c, b = d, c, b, NORM(ROL(XOR(c, OR(b, NOT(d))) + (K[j+2] + W[AND(g + 7, 15) + 1] + a), 15) + b) + a, d, c, b = d, c, b, NORM(ROL(XOR(c, OR(b, NOT(d))) + (K[j+3] + W[AND(g - 2, 15) + 1] + a), 21) + b) + end + H[1], H[2], H[3], H[4] = NORM(a + H[1]), NORM(b + H[2]), NORM(c + H[3]), NORM(d + H[4]) + end + end + + + -- SHA-1 implementation for "LuaJIT without FFI" branch + + function sha1_feed_64(H, str, offs, size) + -- offs >= 0, size >= 0, size is multiple of 64 + local W = common_W + for pos = offs, offs + size - 1, 64 do + for j = 1, 16 do + pos = pos + 4 + local a, b, c, d = byte(str, pos - 3, pos) + W[j] = OR(SHL(a, 24), SHL(b, 16), SHL(c, 8), d) + end + for j = 17, 80 do + W[j] = ROL(XOR(W[j-3], W[j-8], W[j-14], W[j-16]), 1) + end + local a, b, c, d, e = H[1], H[2], H[3], H[4], H[5] + for j = 1, 20, 5 do + e, d, c, b, a = d, c, ROR(b, 2), a, NORM(ROL(a, 5) + XOR(d, AND(b, XOR(d, c))) + (W[j] + 0x5A827999 + e)) -- constant = floor(2^30 * sqrt(2)) + e, d, c, b, a = d, c, ROR(b, 2), a, NORM(ROL(a, 5) + XOR(d, AND(b, XOR(d, c))) + (W[j+1] + 0x5A827999 + e)) + e, d, c, b, a = d, c, ROR(b, 2), a, NORM(ROL(a, 5) + XOR(d, AND(b, XOR(d, c))) + (W[j+2] + 0x5A827999 + e)) + e, d, c, b, a = d, c, ROR(b, 2), a, NORM(ROL(a, 5) + XOR(d, AND(b, XOR(d, c))) + (W[j+3] + 0x5A827999 + e)) + e, d, c, b, a = d, c, ROR(b, 2), a, NORM(ROL(a, 5) + XOR(d, AND(b, XOR(d, c))) + (W[j+4] + 0x5A827999 + e)) + end + for j = 21, 40, 5 do + e, d, c, b, a = d, c, ROR(b, 2), a, NORM(ROL(a, 5) + XOR(b, c, d) + (W[j] + 0x6ED9EBA1 + e)) -- 2^30 * sqrt(3) + e, d, c, b, a = d, c, ROR(b, 2), a, NORM(ROL(a, 5) + XOR(b, c, d) + (W[j+1] + 0x6ED9EBA1 + e)) + e, d, c, b, a = d, c, ROR(b, 2), a, NORM(ROL(a, 5) + XOR(b, c, d) + (W[j+2] + 0x6ED9EBA1 + e)) + e, d, c, b, a = d, c, ROR(b, 2), a, NORM(ROL(a, 5) + XOR(b, c, d) + (W[j+3] + 0x6ED9EBA1 + e)) + e, d, c, b, a = d, c, ROR(b, 2), a, NORM(ROL(a, 5) + XOR(b, c, d) + (W[j+4] + 0x6ED9EBA1 + e)) + end + for j = 41, 60, 5 do + e, d, c, b, a = d, c, ROR(b, 2), a, NORM(ROL(a, 5) + XOR(AND(d, XOR(b, c)), AND(b, c)) + (W[j] + 0x8F1BBCDC + e)) -- 2^30 * sqrt(5) + e, d, c, b, a = d, c, ROR(b, 2), a, NORM(ROL(a, 5) + XOR(AND(d, XOR(b, c)), AND(b, c)) + (W[j+1] + 0x8F1BBCDC + e)) + e, d, c, b, a = d, c, ROR(b, 2), a, NORM(ROL(a, 5) + XOR(AND(d, XOR(b, c)), AND(b, c)) + (W[j+2] + 0x8F1BBCDC + e)) + e, d, c, b, a = d, c, ROR(b, 2), a, NORM(ROL(a, 5) + XOR(AND(d, XOR(b, c)), AND(b, c)) + (W[j+3] + 0x8F1BBCDC + e)) + e, d, c, b, a = d, c, ROR(b, 2), a, NORM(ROL(a, 5) + XOR(AND(d, XOR(b, c)), AND(b, c)) + (W[j+4] + 0x8F1BBCDC + e)) + end + for j = 61, 80, 5 do + e, d, c, b, a = d, c, ROR(b, 2), a, NORM(ROL(a, 5) + XOR(b, c, d) + (W[j] + 0xCA62C1D6 + e)) -- 2^30 * sqrt(10) + e, d, c, b, a = d, c, ROR(b, 2), a, NORM(ROL(a, 5) + XOR(b, c, d) + (W[j+1] + 0xCA62C1D6 + e)) + e, d, c, b, a = d, c, ROR(b, 2), a, NORM(ROL(a, 5) + XOR(b, c, d) + (W[j+2] + 0xCA62C1D6 + e)) + e, d, c, b, a = d, c, ROR(b, 2), a, NORM(ROL(a, 5) + XOR(b, c, d) + (W[j+3] + 0xCA62C1D6 + e)) + e, d, c, b, a = d, c, ROR(b, 2), a, NORM(ROL(a, 5) + XOR(b, c, d) + (W[j+4] + 0xCA62C1D6 + e)) + end + H[1], H[2], H[3], H[4], H[5] = NORM(a + H[1]), NORM(b + H[2]), NORM(c + H[3]), NORM(d + H[4]), NORM(e + H[5]) + end + end + + + -- BLAKE2b implementation for "LuaJIT without FFI" branch + + do + local v_lo, v_hi = {}, {} + + local function G(a, b, c, d, k1, k2) + local W = common_W + local va_lo, vb_lo, vc_lo, vd_lo = v_lo[a], v_lo[b], v_lo[c], v_lo[d] + local va_hi, vb_hi, vc_hi, vd_hi = v_hi[a], v_hi[b], v_hi[c], v_hi[d] + local z = W[2*k1-1] + (va_lo % 2^32 + vb_lo % 2^32) + va_lo = NORM(z) + va_hi = NORM(W[2*k1] + (va_hi + vb_hi + floor(z / 2^32))) + vd_lo, vd_hi = XOR(vd_hi, va_hi), XOR(vd_lo, va_lo) + z = vc_lo % 2^32 + vd_lo % 2^32 + vc_lo = NORM(z) + vc_hi = NORM(vc_hi + vd_hi + floor(z / 2^32)) + vb_lo, vb_hi = XOR(vb_lo, vc_lo), XOR(vb_hi, vc_hi) + vb_lo, vb_hi = XOR(SHR(vb_lo, 24), SHL(vb_hi, 8)), XOR(SHR(vb_hi, 24), SHL(vb_lo, 8)) + z = W[2*k2-1] + (va_lo % 2^32 + vb_lo % 2^32) + va_lo = NORM(z) + va_hi = NORM(W[2*k2] + (va_hi + vb_hi + floor(z / 2^32))) + vd_lo, vd_hi = XOR(vd_lo, va_lo), XOR(vd_hi, va_hi) + vd_lo, vd_hi = XOR(SHR(vd_lo, 16), SHL(vd_hi, 16)), XOR(SHR(vd_hi, 16), SHL(vd_lo, 16)) + z = vc_lo % 2^32 + vd_lo % 2^32 + vc_lo = NORM(z) + vc_hi = NORM(vc_hi + vd_hi + floor(z / 2^32)) + vb_lo, vb_hi = XOR(vb_lo, vc_lo), XOR(vb_hi, vc_hi) + vb_lo, vb_hi = XOR(SHL(vb_lo, 1), SHR(vb_hi, 31)), XOR(SHL(vb_hi, 1), SHR(vb_lo, 31)) + v_lo[a], v_lo[b], v_lo[c], v_lo[d] = va_lo, vb_lo, vc_lo, vd_lo + v_hi[a], v_hi[b], v_hi[c], v_hi[d] = va_hi, vb_hi, vc_hi, vd_hi + end + + function blake2b_feed_128(H_lo, H_hi, str, offs, size, bytes_compressed, last_block_size, is_last_node) + -- offs >= 0, size >= 0, size is multiple of 128 + local W = common_W + local h1_lo, h2_lo, h3_lo, h4_lo, h5_lo, h6_lo, h7_lo, h8_lo = H_lo[1], H_lo[2], H_lo[3], H_lo[4], H_lo[5], H_lo[6], H_lo[7], H_lo[8] + local h1_hi, h2_hi, h3_hi, h4_hi, h5_hi, h6_hi, h7_hi, h8_hi = H_hi[1], H_hi[2], H_hi[3], H_hi[4], H_hi[5], H_hi[6], H_hi[7], H_hi[8] + for pos = offs, offs + size - 1, 128 do + if str then + for j = 1, 32 do + pos = pos + 4 + local a, b, c, d = byte(str, pos - 3, pos) + W[j] = d * 2^24 + OR(SHL(c, 16), SHL(b, 8), a) + end + end + v_lo[0x0], v_lo[0x1], v_lo[0x2], v_lo[0x3], v_lo[0x4], v_lo[0x5], v_lo[0x6], v_lo[0x7] = h1_lo, h2_lo, h3_lo, h4_lo, h5_lo, h6_lo, h7_lo, h8_lo + v_lo[0x8], v_lo[0x9], v_lo[0xA], v_lo[0xB], v_lo[0xC], v_lo[0xD], v_lo[0xE], v_lo[0xF] = sha2_H_lo[1], sha2_H_lo[2], sha2_H_lo[3], sha2_H_lo[4], sha2_H_lo[5], sha2_H_lo[6], sha2_H_lo[7], sha2_H_lo[8] + v_hi[0x0], v_hi[0x1], v_hi[0x2], v_hi[0x3], v_hi[0x4], v_hi[0x5], v_hi[0x6], v_hi[0x7] = h1_hi, h2_hi, h3_hi, h4_hi, h5_hi, h6_hi, h7_hi, h8_hi + v_hi[0x8], v_hi[0x9], v_hi[0xA], v_hi[0xB], v_hi[0xC], v_hi[0xD], v_hi[0xE], v_hi[0xF] = sha2_H_hi[1], sha2_H_hi[2], sha2_H_hi[3], sha2_H_hi[4], sha2_H_hi[5], sha2_H_hi[6], sha2_H_hi[7], sha2_H_hi[8] + bytes_compressed = bytes_compressed + (last_block_size or 128) + local t0_lo = bytes_compressed % 2^32 + local t0_hi = floor(bytes_compressed / 2^32) + v_lo[0xC] = XOR(v_lo[0xC], t0_lo) -- t0 = low_8_bytes(bytes_compressed) + v_hi[0xC] = XOR(v_hi[0xC], t0_hi) + -- t1 = high_8_bytes(bytes_compressed) = 0, message length is always below 2^53 bytes + if last_block_size then -- flag f0 + v_lo[0xE] = NOT(v_lo[0xE]) + v_hi[0xE] = NOT(v_hi[0xE]) + end + if is_last_node then -- flag f1 + v_lo[0xF] = NOT(v_lo[0xF]) + v_hi[0xF] = NOT(v_hi[0xF]) + end + for j = 1, 12 do + local row = sigma[j] + G(0, 4, 8, 12, row[ 1], row[ 2]) + G(1, 5, 9, 13, row[ 3], row[ 4]) + G(2, 6, 10, 14, row[ 5], row[ 6]) + G(3, 7, 11, 15, row[ 7], row[ 8]) + G(0, 5, 10, 15, row[ 9], row[10]) + G(1, 6, 11, 12, row[11], row[12]) + G(2, 7, 8, 13, row[13], row[14]) + G(3, 4, 9, 14, row[15], row[16]) + end + h1_lo = XOR(h1_lo, v_lo[0x0], v_lo[0x8]) + h2_lo = XOR(h2_lo, v_lo[0x1], v_lo[0x9]) + h3_lo = XOR(h3_lo, v_lo[0x2], v_lo[0xA]) + h4_lo = XOR(h4_lo, v_lo[0x3], v_lo[0xB]) + h5_lo = XOR(h5_lo, v_lo[0x4], v_lo[0xC]) + h6_lo = XOR(h6_lo, v_lo[0x5], v_lo[0xD]) + h7_lo = XOR(h7_lo, v_lo[0x6], v_lo[0xE]) + h8_lo = XOR(h8_lo, v_lo[0x7], v_lo[0xF]) + h1_hi = XOR(h1_hi, v_hi[0x0], v_hi[0x8]) + h2_hi = XOR(h2_hi, v_hi[0x1], v_hi[0x9]) + h3_hi = XOR(h3_hi, v_hi[0x2], v_hi[0xA]) + h4_hi = XOR(h4_hi, v_hi[0x3], v_hi[0xB]) + h5_hi = XOR(h5_hi, v_hi[0x4], v_hi[0xC]) + h6_hi = XOR(h6_hi, v_hi[0x5], v_hi[0xD]) + h7_hi = XOR(h7_hi, v_hi[0x6], v_hi[0xE]) + h8_hi = XOR(h8_hi, v_hi[0x7], v_hi[0xF]) + end + H_lo[1], H_lo[2], H_lo[3], H_lo[4], H_lo[5], H_lo[6], H_lo[7], H_lo[8] = h1_lo % 2^32, h2_lo % 2^32, h3_lo % 2^32, h4_lo % 2^32, h5_lo % 2^32, h6_lo % 2^32, h7_lo % 2^32, h8_lo % 2^32 + H_hi[1], H_hi[2], H_hi[3], H_hi[4], H_hi[5], H_hi[6], H_hi[7], H_hi[8] = h1_hi % 2^32, h2_hi % 2^32, h3_hi % 2^32, h4_hi % 2^32, h5_hi % 2^32, h6_hi % 2^32, h7_hi % 2^32, h8_hi % 2^32 + return bytes_compressed + end + + end +end + + +if branch == "FFI" or branch == "LJ" then + + + -- BLAKE2s and BLAKE3 implementations for "LuaJIT with FFI" and "LuaJIT without FFI" branches + + do + local W = common_W_blake2s + local v = v_for_blake2s_feed_64 + + local function G(a, b, c, d, k1, k2) + local va, vb, vc, vd = v[a], v[b], v[c], v[d] + va = NORM(W[k1] + (va + vb)) + vd = ROR(XOR(vd, va), 16) + vc = NORM(vc + vd) + vb = ROR(XOR(vb, vc), 12) + va = NORM(W[k2] + (va + vb)) + vd = ROR(XOR(vd, va), 8) + vc = NORM(vc + vd) + vb = ROR(XOR(vb, vc), 7) + v[a], v[b], v[c], v[d] = va, vb, vc, vd + end + + function blake2s_feed_64(H, str, offs, size, bytes_compressed, last_block_size, is_last_node) + -- offs >= 0, size >= 0, size is multiple of 64 + local h1, h2, h3, h4, h5, h6, h7, h8 = NORM(H[1]), NORM(H[2]), NORM(H[3]), NORM(H[4]), NORM(H[5]), NORM(H[6]), NORM(H[7]), NORM(H[8]) + for pos = offs, offs + size - 1, 64 do + if str then + for j = 1, 16 do + pos = pos + 4 + local a, b, c, d = byte(str, pos - 3, pos) + W[j] = OR(SHL(d, 24), SHL(c, 16), SHL(b, 8), a) + end + end + v[0x0], v[0x1], v[0x2], v[0x3], v[0x4], v[0x5], v[0x6], v[0x7] = h1, h2, h3, h4, h5, h6, h7, h8 + v[0x8], v[0x9], v[0xA], v[0xB], v[0xE], v[0xF] = NORM(sha2_H_hi[1]), NORM(sha2_H_hi[2]), NORM(sha2_H_hi[3]), NORM(sha2_H_hi[4]), NORM(sha2_H_hi[7]), NORM(sha2_H_hi[8]) + bytes_compressed = bytes_compressed + (last_block_size or 64) + local t0 = bytes_compressed % 2^32 + local t1 = floor(bytes_compressed / 2^32) + v[0xC] = XOR(sha2_H_hi[5], t0) -- t0 = low_4_bytes(bytes_compressed) + v[0xD] = XOR(sha2_H_hi[6], t1) -- t1 = high_4_bytes(bytes_compressed + if last_block_size then -- flag f0 + v[0xE] = NOT(v[0xE]) + end + if is_last_node then -- flag f1 + v[0xF] = NOT(v[0xF]) + end + for j = 1, 10 do + local row = sigma[j] + G(0, 4, 8, 12, row[ 1], row[ 2]) + G(1, 5, 9, 13, row[ 3], row[ 4]) + G(2, 6, 10, 14, row[ 5], row[ 6]) + G(3, 7, 11, 15, row[ 7], row[ 8]) + G(0, 5, 10, 15, row[ 9], row[10]) + G(1, 6, 11, 12, row[11], row[12]) + G(2, 7, 8, 13, row[13], row[14]) + G(3, 4, 9, 14, row[15], row[16]) + end + h1 = XOR(h1, v[0x0], v[0x8]) + h2 = XOR(h2, v[0x1], v[0x9]) + h3 = XOR(h3, v[0x2], v[0xA]) + h4 = XOR(h4, v[0x3], v[0xB]) + h5 = XOR(h5, v[0x4], v[0xC]) + h6 = XOR(h6, v[0x5], v[0xD]) + h7 = XOR(h7, v[0x6], v[0xE]) + h8 = XOR(h8, v[0x7], v[0xF]) + end + H[1], H[2], H[3], H[4], H[5], H[6], H[7], H[8] = h1, h2, h3, h4, h5, h6, h7, h8 + return bytes_compressed + end + + function blake3_feed_64(str, offs, size, flags, chunk_index, H_in, H_out, wide_output, block_length) + -- offs >= 0, size >= 0, size is multiple of 64 + block_length = block_length or 64 + local h1, h2, h3, h4, h5, h6, h7, h8 = NORM(H_in[1]), NORM(H_in[2]), NORM(H_in[3]), NORM(H_in[4]), NORM(H_in[5]), NORM(H_in[6]), NORM(H_in[7]), NORM(H_in[8]) + H_out = H_out or H_in + for pos = offs, offs + size - 1, 64 do + if str then + for j = 1, 16 do + pos = pos + 4 + local a, b, c, d = byte(str, pos - 3, pos) + W[j] = OR(SHL(d, 24), SHL(c, 16), SHL(b, 8), a) + end + end + v[0x0], v[0x1], v[0x2], v[0x3], v[0x4], v[0x5], v[0x6], v[0x7] = h1, h2, h3, h4, h5, h6, h7, h8 + v[0x8], v[0x9], v[0xA], v[0xB] = NORM(sha2_H_hi[1]), NORM(sha2_H_hi[2]), NORM(sha2_H_hi[3]), NORM(sha2_H_hi[4]) + v[0xC] = NORM(chunk_index % 2^32) -- t0 = low_4_bytes(chunk_index) + v[0xD] = floor(chunk_index / 2^32) -- t1 = high_4_bytes(chunk_index) + v[0xE], v[0xF] = block_length, flags + for j = 1, 7 do + G(0, 4, 8, 12, perm_blake3[j], perm_blake3[j + 14]) + G(1, 5, 9, 13, perm_blake3[j + 1], perm_blake3[j + 2]) + G(2, 6, 10, 14, perm_blake3[j + 16], perm_blake3[j + 7]) + G(3, 7, 11, 15, perm_blake3[j + 15], perm_blake3[j + 17]) + G(0, 5, 10, 15, perm_blake3[j + 21], perm_blake3[j + 5]) + G(1, 6, 11, 12, perm_blake3[j + 3], perm_blake3[j + 6]) + G(2, 7, 8, 13, perm_blake3[j + 4], perm_blake3[j + 18]) + G(3, 4, 9, 14, perm_blake3[j + 19], perm_blake3[j + 20]) + end + if wide_output then + H_out[ 9] = XOR(h1, v[0x8]) + H_out[10] = XOR(h2, v[0x9]) + H_out[11] = XOR(h3, v[0xA]) + H_out[12] = XOR(h4, v[0xB]) + H_out[13] = XOR(h5, v[0xC]) + H_out[14] = XOR(h6, v[0xD]) + H_out[15] = XOR(h7, v[0xE]) + H_out[16] = XOR(h8, v[0xF]) + end + h1 = XOR(v[0x0], v[0x8]) + h2 = XOR(v[0x1], v[0x9]) + h3 = XOR(v[0x2], v[0xA]) + h4 = XOR(v[0x3], v[0xB]) + h5 = XOR(v[0x4], v[0xC]) + h6 = XOR(v[0x5], v[0xD]) + h7 = XOR(v[0x6], v[0xE]) + h8 = XOR(v[0x7], v[0xF]) + end + H_out[1], H_out[2], H_out[3], H_out[4], H_out[5], H_out[6], H_out[7], H_out[8] = h1, h2, h3, h4, h5, h6, h7, h8 + end + + end + +end + + +if branch == "INT64" then + + + -- implementation for Lua 5.3/5.4 + + hi_factor = 4294967296 + hi_factor_keccak = 4294967296 + lanes_index_base = 1 + + HEX64, XORA5, XOR_BYTE, sha256_feed_64, sha512_feed_128, md5_feed_64, sha1_feed_64, keccak_feed, blake2s_feed_64, blake2b_feed_128, blake3_feed_64 = load[=[-- branch "INT64" + local md5_next_shift, md5_K, sha2_K_lo, sha2_K_hi, build_keccak_format, sha3_RC_lo, sigma, common_W, sha2_H_lo, sha2_H_hi, perm_blake3 = ... + local string_format, string_unpack = string.format, string.unpack + + local function HEX64(x) + return string_format("%016x", x) + end + + local function XORA5(x, y) + return x ~ (y or 0xa5a5a5a5a5a5a5a5) + end + + local function XOR_BYTE(x, y) + return x ~ y + end + + local function sha256_feed_64(H, str, offs, size) + -- offs >= 0, size >= 0, size is multiple of 64 + local W, K = common_W, sha2_K_hi + local h1, h2, h3, h4, h5, h6, h7, h8 = H[1], H[2], H[3], H[4], H[5], H[6], H[7], H[8] + for pos = offs + 1, offs + size, 64 do + W[1], W[2], W[3], W[4], W[5], W[6], W[7], W[8], W[9], W[10], W[11], W[12], W[13], W[14], W[15], W[16] = + string_unpack(">I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4", str, pos) + for j = 17, 64 do + local a = W[j-15] + a = a<<32 | a + local b = W[j-2] + b = b<<32 | b + W[j] = (a>>7 ~ a>>18 ~ a>>35) + (b>>17 ~ b>>19 ~ b>>42) + W[j-7] + W[j-16] & (1<<32)-1 + end + local a, b, c, d, e, f, g, h = h1, h2, h3, h4, h5, h6, h7, h8 + for j = 1, 64 do + e = e<<32 | e & (1<<32)-1 + local z = (e>>6 ~ e>>11 ~ e>>25) + (g ~ e & (f ~ g)) + h + K[j] + W[j] + h = g + g = f + f = e + e = z + d + d = c + c = b + b = a + a = a<<32 | a & (1<<32)-1 + a = z + ((a ~ c) & d ~ a & c) + (a>>2 ~ a>>13 ~ a>>22) + end + h1 = a + h1 + h2 = b + h2 + h3 = c + h3 + h4 = d + h4 + h5 = e + h5 + h6 = f + h6 + h7 = g + h7 + h8 = h + h8 + end + H[1], H[2], H[3], H[4], H[5], H[6], H[7], H[8] = h1, h2, h3, h4, h5, h6, h7, h8 + end + + local function sha512_feed_128(H, _, str, offs, size) + -- offs >= 0, size >= 0, size is multiple of 128 + local W, K = common_W, sha2_K_lo + local h1, h2, h3, h4, h5, h6, h7, h8 = H[1], H[2], H[3], H[4], H[5], H[6], H[7], H[8] + for pos = offs + 1, offs + size, 128 do + W[1], W[2], W[3], W[4], W[5], W[6], W[7], W[8], W[9], W[10], W[11], W[12], W[13], W[14], W[15], W[16] = + string_unpack(">i8i8i8i8i8i8i8i8i8i8i8i8i8i8i8i8", str, pos) + for j = 17, 80 do + local a = W[j-15] + local b = W[j-2] + W[j] = (a >> 1 ~ a >> 7 ~ a >> 8 ~ a << 56 ~ a << 63) + (b >> 6 ~ b >> 19 ~ b >> 61 ~ b << 3 ~ b << 45) + W[j-7] + W[j-16] + end + local a, b, c, d, e, f, g, h = h1, h2, h3, h4, h5, h6, h7, h8 + for j = 1, 80 do + local z = (e >> 14 ~ e >> 18 ~ e >> 41 ~ e << 23 ~ e << 46 ~ e << 50) + (g ~ e & (f ~ g)) + h + K[j] + W[j] + h = g + g = f + f = e + e = z + d + d = c + c = b + b = a + a = z + ((a ~ c) & d ~ a & c) + (a >> 28 ~ a >> 34 ~ a >> 39 ~ a << 25 ~ a << 30 ~ a << 36) + end + h1 = a + h1 + h2 = b + h2 + h3 = c + h3 + h4 = d + h4 + h5 = e + h5 + h6 = f + h6 + h7 = g + h7 + h8 = h + h8 + end + H[1], H[2], H[3], H[4], H[5], H[6], H[7], H[8] = h1, h2, h3, h4, h5, h6, h7, h8 + end + + local function md5_feed_64(H, str, offs, size) + -- offs >= 0, size >= 0, size is multiple of 64 + local W, K, md5_next_shift = common_W, md5_K, md5_next_shift + local h1, h2, h3, h4 = H[1], H[2], H[3], H[4] + for pos = offs + 1, offs + size, 64 do + W[1], W[2], W[3], W[4], W[5], W[6], W[7], W[8], W[9], W[10], W[11], W[12], W[13], W[14], W[15], W[16] = + string_unpack("> s) + b + s = md5_next_shift[s] + end + s = 32-5 + for j = 17, 32 do + local F = (c ~ d & (b ~ c)) + a + K[j] + W[(5*j-4 & 15) + 1] + a = d + d = c + c = b + b = ((F<<32 | F & (1<<32)-1) >> s) + b + s = md5_next_shift[s] + end + s = 32-4 + for j = 33, 48 do + local F = (b ~ c ~ d) + a + K[j] + W[(3*j+2 & 15) + 1] + a = d + d = c + c = b + b = ((F<<32 | F & (1<<32)-1) >> s) + b + s = md5_next_shift[s] + end + s = 32-6 + for j = 49, 64 do + local F = (c ~ (b | ~d)) + a + K[j] + W[(j*7-7 & 15) + 1] + a = d + d = c + c = b + b = ((F<<32 | F & (1<<32)-1) >> s) + b + s = md5_next_shift[s] + end + h1 = a + h1 + h2 = b + h2 + h3 = c + h3 + h4 = d + h4 + end + H[1], H[2], H[3], H[4] = h1, h2, h3, h4 + end + + local function sha1_feed_64(H, str, offs, size) + -- offs >= 0, size >= 0, size is multiple of 64 + local W = common_W + local h1, h2, h3, h4, h5 = H[1], H[2], H[3], H[4], H[5] + for pos = offs + 1, offs + size, 64 do + W[1], W[2], W[3], W[4], W[5], W[6], W[7], W[8], W[9], W[10], W[11], W[12], W[13], W[14], W[15], W[16] = + string_unpack(">I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4", str, pos) + for j = 17, 80 do + local a = W[j-3] ~ W[j-8] ~ W[j-14] ~ W[j-16] + W[j] = (a<<32 | a) << 1 >> 32 + end + local a, b, c, d, e = h1, h2, h3, h4, h5 + for j = 1, 20 do + local z = ((a<<32 | a & (1<<32)-1) >> 27) + (d ~ b & (c ~ d)) + 0x5A827999 + W[j] + e -- constant = floor(2^30 * sqrt(2)) + e = d + d = c + c = (b<<32 | b & (1<<32)-1) >> 2 + b = a + a = z + end + for j = 21, 40 do + local z = ((a<<32 | a & (1<<32)-1) >> 27) + (b ~ c ~ d) + 0x6ED9EBA1 + W[j] + e -- 2^30 * sqrt(3) + e = d + d = c + c = (b<<32 | b & (1<<32)-1) >> 2 + b = a + a = z + end + for j = 41, 60 do + local z = ((a<<32 | a & (1<<32)-1) >> 27) + ((b ~ c) & d ~ b & c) + 0x8F1BBCDC + W[j] + e -- 2^30 * sqrt(5) + e = d + d = c + c = (b<<32 | b & (1<<32)-1) >> 2 + b = a + a = z + end + for j = 61, 80 do + local z = ((a<<32 | a & (1<<32)-1) >> 27) + (b ~ c ~ d) + 0xCA62C1D6 + W[j] + e -- 2^30 * sqrt(10) + e = d + d = c + c = (b<<32 | b & (1<<32)-1) >> 2 + b = a + a = z + end + h1 = a + h1 + h2 = b + h2 + h3 = c + h3 + h4 = d + h4 + h5 = e + h5 + end + H[1], H[2], H[3], H[4], H[5] = h1, h2, h3, h4, h5 + end + + local keccak_format_i8 = build_keccak_format("i8") + + local function keccak_feed(lanes, _, str, offs, size, block_size_in_bytes) + -- offs >= 0, size >= 0, size is multiple of block_size_in_bytes, block_size_in_bytes is positive multiple of 8 + local RC = sha3_RC_lo + local qwords_qty = block_size_in_bytes / 8 + local keccak_format = keccak_format_i8[qwords_qty] + for pos = offs + 1, offs + size, block_size_in_bytes do + local qwords_from_message = {string_unpack(keccak_format, str, pos)} + for j = 1, qwords_qty do + lanes[j] = lanes[j] ~ qwords_from_message[j] + end + local L01, L02, L03, L04, L05, L06, L07, L08, L09, L10, L11, L12, L13, L14, L15, L16, L17, L18, L19, L20, L21, L22, L23, L24, L25 = + lanes[1], lanes[2], lanes[3], lanes[4], lanes[5], lanes[6], lanes[7], lanes[8], lanes[9], lanes[10], lanes[11], lanes[12], lanes[13], + lanes[14], lanes[15], lanes[16], lanes[17], lanes[18], lanes[19], lanes[20], lanes[21], lanes[22], lanes[23], lanes[24], lanes[25] + for round_idx = 1, 24 do + local C1 = L01 ~ L06 ~ L11 ~ L16 ~ L21 + local C2 = L02 ~ L07 ~ L12 ~ L17 ~ L22 + local C3 = L03 ~ L08 ~ L13 ~ L18 ~ L23 + local C4 = L04 ~ L09 ~ L14 ~ L19 ~ L24 + local C5 = L05 ~ L10 ~ L15 ~ L20 ~ L25 + local D = C1 ~ C3<<1 ~ C3>>63 + local T0 = D ~ L02 + local T1 = D ~ L07 + local T2 = D ~ L12 + local T3 = D ~ L17 + local T4 = D ~ L22 + L02 = T1<<44 ~ T1>>20 + L07 = T3<<45 ~ T3>>19 + L12 = T0<<1 ~ T0>>63 + L17 = T2<<10 ~ T2>>54 + L22 = T4<<2 ~ T4>>62 + D = C2 ~ C4<<1 ~ C4>>63 + T0 = D ~ L03 + T1 = D ~ L08 + T2 = D ~ L13 + T3 = D ~ L18 + T4 = D ~ L23 + L03 = T2<<43 ~ T2>>21 + L08 = T4<<61 ~ T4>>3 + L13 = T1<<6 ~ T1>>58 + L18 = T3<<15 ~ T3>>49 + L23 = T0<<62 ~ T0>>2 + D = C3 ~ C5<<1 ~ C5>>63 + T0 = D ~ L04 + T1 = D ~ L09 + T2 = D ~ L14 + T3 = D ~ L19 + T4 = D ~ L24 + L04 = T3<<21 ~ T3>>43 + L09 = T0<<28 ~ T0>>36 + L14 = T2<<25 ~ T2>>39 + L19 = T4<<56 ~ T4>>8 + L24 = T1<<55 ~ T1>>9 + D = C4 ~ C1<<1 ~ C1>>63 + T0 = D ~ L05 + T1 = D ~ L10 + T2 = D ~ L15 + T3 = D ~ L20 + T4 = D ~ L25 + L05 = T4<<14 ~ T4>>50 + L10 = T1<<20 ~ T1>>44 + L15 = T3<<8 ~ T3>>56 + L20 = T0<<27 ~ T0>>37 + L25 = T2<<39 ~ T2>>25 + D = C5 ~ C2<<1 ~ C2>>63 + T1 = D ~ L06 + T2 = D ~ L11 + T3 = D ~ L16 + T4 = D ~ L21 + L06 = T2<<3 ~ T2>>61 + L11 = T4<<18 ~ T4>>46 + L16 = T1<<36 ~ T1>>28 + L21 = T3<<41 ~ T3>>23 + L01 = D ~ L01 + L01, L02, L03, L04, L05 = L01 ~ ~L02 & L03, L02 ~ ~L03 & L04, L03 ~ ~L04 & L05, L04 ~ ~L05 & L01, L05 ~ ~L01 & L02 + L06, L07, L08, L09, L10 = L09 ~ ~L10 & L06, L10 ~ ~L06 & L07, L06 ~ ~L07 & L08, L07 ~ ~L08 & L09, L08 ~ ~L09 & L10 + L11, L12, L13, L14, L15 = L12 ~ ~L13 & L14, L13 ~ ~L14 & L15, L14 ~ ~L15 & L11, L15 ~ ~L11 & L12, L11 ~ ~L12 & L13 + L16, L17, L18, L19, L20 = L20 ~ ~L16 & L17, L16 ~ ~L17 & L18, L17 ~ ~L18 & L19, L18 ~ ~L19 & L20, L19 ~ ~L20 & L16 + L21, L22, L23, L24, L25 = L23 ~ ~L24 & L25, L24 ~ ~L25 & L21, L25 ~ ~L21 & L22, L21 ~ ~L22 & L23, L22 ~ ~L23 & L24 + L01 = L01 ~ RC[round_idx] + end + lanes[1] = L01 + lanes[2] = L02 + lanes[3] = L03 + lanes[4] = L04 + lanes[5] = L05 + lanes[6] = L06 + lanes[7] = L07 + lanes[8] = L08 + lanes[9] = L09 + lanes[10] = L10 + lanes[11] = L11 + lanes[12] = L12 + lanes[13] = L13 + lanes[14] = L14 + lanes[15] = L15 + lanes[16] = L16 + lanes[17] = L17 + lanes[18] = L18 + lanes[19] = L19 + lanes[20] = L20 + lanes[21] = L21 + lanes[22] = L22 + lanes[23] = L23 + lanes[24] = L24 + lanes[25] = L25 + end + end + + local function blake2s_feed_64(H, str, offs, size, bytes_compressed, last_block_size, is_last_node) + -- offs >= 0, size >= 0, size is multiple of 64 + local W = common_W + local h1, h2, h3, h4, h5, h6, h7, h8 = H[1], H[2], H[3], H[4], H[5], H[6], H[7], H[8] + for pos = offs + 1, offs + size, 64 do + if str then + W[1], W[2], W[3], W[4], W[5], W[6], W[7], W[8], W[9], W[10], W[11], W[12], W[13], W[14], W[15], W[16] = + string_unpack("> 32 -- t1 = high_4_bytes(bytes_compressed) + if last_block_size then -- flag f0 + vE = ~vE + end + if is_last_node then -- flag f1 + vF = ~vF + end + for j = 1, 10 do + local row = sigma[j] + v0 = v0 + v4 + W[row[1]] + vC = vC ~ v0 + vC = (vC & (1<<32)-1) >> 16 | vC << 16 + v8 = v8 + vC + v4 = v4 ~ v8 + v4 = (v4 & (1<<32)-1) >> 12 | v4 << 20 + v0 = v0 + v4 + W[row[2]] + vC = vC ~ v0 + vC = (vC & (1<<32)-1) >> 8 | vC << 24 + v8 = v8 + vC + v4 = v4 ~ v8 + v4 = (v4 & (1<<32)-1) >> 7 | v4 << 25 + v1 = v1 + v5 + W[row[3]] + vD = vD ~ v1 + vD = (vD & (1<<32)-1) >> 16 | vD << 16 + v9 = v9 + vD + v5 = v5 ~ v9 + v5 = (v5 & (1<<32)-1) >> 12 | v5 << 20 + v1 = v1 + v5 + W[row[4]] + vD = vD ~ v1 + vD = (vD & (1<<32)-1) >> 8 | vD << 24 + v9 = v9 + vD + v5 = v5 ~ v9 + v5 = (v5 & (1<<32)-1) >> 7 | v5 << 25 + v2 = v2 + v6 + W[row[5]] + vE = vE ~ v2 + vE = (vE & (1<<32)-1) >> 16 | vE << 16 + vA = vA + vE + v6 = v6 ~ vA + v6 = (v6 & (1<<32)-1) >> 12 | v6 << 20 + v2 = v2 + v6 + W[row[6]] + vE = vE ~ v2 + vE = (vE & (1<<32)-1) >> 8 | vE << 24 + vA = vA + vE + v6 = v6 ~ vA + v6 = (v6 & (1<<32)-1) >> 7 | v6 << 25 + v3 = v3 + v7 + W[row[7]] + vF = vF ~ v3 + vF = (vF & (1<<32)-1) >> 16 | vF << 16 + vB = vB + vF + v7 = v7 ~ vB + v7 = (v7 & (1<<32)-1) >> 12 | v7 << 20 + v3 = v3 + v7 + W[row[8]] + vF = vF ~ v3 + vF = (vF & (1<<32)-1) >> 8 | vF << 24 + vB = vB + vF + v7 = v7 ~ vB + v7 = (v7 & (1<<32)-1) >> 7 | v7 << 25 + v0 = v0 + v5 + W[row[9]] + vF = vF ~ v0 + vF = (vF & (1<<32)-1) >> 16 | vF << 16 + vA = vA + vF + v5 = v5 ~ vA + v5 = (v5 & (1<<32)-1) >> 12 | v5 << 20 + v0 = v0 + v5 + W[row[10]] + vF = vF ~ v0 + vF = (vF & (1<<32)-1) >> 8 | vF << 24 + vA = vA + vF + v5 = v5 ~ vA + v5 = (v5 & (1<<32)-1) >> 7 | v5 << 25 + v1 = v1 + v6 + W[row[11]] + vC = vC ~ v1 + vC = (vC & (1<<32)-1) >> 16 | vC << 16 + vB = vB + vC + v6 = v6 ~ vB + v6 = (v6 & (1<<32)-1) >> 12 | v6 << 20 + v1 = v1 + v6 + W[row[12]] + vC = vC ~ v1 + vC = (vC & (1<<32)-1) >> 8 | vC << 24 + vB = vB + vC + v6 = v6 ~ vB + v6 = (v6 & (1<<32)-1) >> 7 | v6 << 25 + v2 = v2 + v7 + W[row[13]] + vD = vD ~ v2 + vD = (vD & (1<<32)-1) >> 16 | vD << 16 + v8 = v8 + vD + v7 = v7 ~ v8 + v7 = (v7 & (1<<32)-1) >> 12 | v7 << 20 + v2 = v2 + v7 + W[row[14]] + vD = vD ~ v2 + vD = (vD & (1<<32)-1) >> 8 | vD << 24 + v8 = v8 + vD + v7 = v7 ~ v8 + v7 = (v7 & (1<<32)-1) >> 7 | v7 << 25 + v3 = v3 + v4 + W[row[15]] + vE = vE ~ v3 + vE = (vE & (1<<32)-1) >> 16 | vE << 16 + v9 = v9 + vE + v4 = v4 ~ v9 + v4 = (v4 & (1<<32)-1) >> 12 | v4 << 20 + v3 = v3 + v4 + W[row[16]] + vE = vE ~ v3 + vE = (vE & (1<<32)-1) >> 8 | vE << 24 + v9 = v9 + vE + v4 = v4 ~ v9 + v4 = (v4 & (1<<32)-1) >> 7 | v4 << 25 + end + h1 = h1 ~ v0 ~ v8 + h2 = h2 ~ v1 ~ v9 + h3 = h3 ~ v2 ~ vA + h4 = h4 ~ v3 ~ vB + h5 = h5 ~ v4 ~ vC + h6 = h6 ~ v5 ~ vD + h7 = h7 ~ v6 ~ vE + h8 = h8 ~ v7 ~ vF + end + H[1], H[2], H[3], H[4], H[5], H[6], H[7], H[8] = h1, h2, h3, h4, h5, h6, h7, h8 + return bytes_compressed + end + + local function blake2b_feed_128(H, _, str, offs, size, bytes_compressed, last_block_size, is_last_node) + -- offs >= 0, size >= 0, size is multiple of 128 + local W = common_W + local h1, h2, h3, h4, h5, h6, h7, h8 = H[1], H[2], H[3], H[4], H[5], H[6], H[7], H[8] + for pos = offs + 1, offs + size, 128 do + if str then + W[1], W[2], W[3], W[4], W[5], W[6], W[7], W[8], W[9], W[10], W[11], W[12], W[13], W[14], W[15], W[16] = + string_unpack("> 32 | vC << 32 + v8 = v8 + vC + v4 = v4 ~ v8 + v4 = v4 >> 24 | v4 << 40 + v0 = v0 + v4 + W[row[2]] + vC = vC ~ v0 + vC = vC >> 16 | vC << 48 + v8 = v8 + vC + v4 = v4 ~ v8 + v4 = v4 >> 63 | v4 << 1 + v1 = v1 + v5 + W[row[3]] + vD = vD ~ v1 + vD = vD >> 32 | vD << 32 + v9 = v9 + vD + v5 = v5 ~ v9 + v5 = v5 >> 24 | v5 << 40 + v1 = v1 + v5 + W[row[4]] + vD = vD ~ v1 + vD = vD >> 16 | vD << 48 + v9 = v9 + vD + v5 = v5 ~ v9 + v5 = v5 >> 63 | v5 << 1 + v2 = v2 + v6 + W[row[5]] + vE = vE ~ v2 + vE = vE >> 32 | vE << 32 + vA = vA + vE + v6 = v6 ~ vA + v6 = v6 >> 24 | v6 << 40 + v2 = v2 + v6 + W[row[6]] + vE = vE ~ v2 + vE = vE >> 16 | vE << 48 + vA = vA + vE + v6 = v6 ~ vA + v6 = v6 >> 63 | v6 << 1 + v3 = v3 + v7 + W[row[7]] + vF = vF ~ v3 + vF = vF >> 32 | vF << 32 + vB = vB + vF + v7 = v7 ~ vB + v7 = v7 >> 24 | v7 << 40 + v3 = v3 + v7 + W[row[8]] + vF = vF ~ v3 + vF = vF >> 16 | vF << 48 + vB = vB + vF + v7 = v7 ~ vB + v7 = v7 >> 63 | v7 << 1 + v0 = v0 + v5 + W[row[9]] + vF = vF ~ v0 + vF = vF >> 32 | vF << 32 + vA = vA + vF + v5 = v5 ~ vA + v5 = v5 >> 24 | v5 << 40 + v0 = v0 + v5 + W[row[10]] + vF = vF ~ v0 + vF = vF >> 16 | vF << 48 + vA = vA + vF + v5 = v5 ~ vA + v5 = v5 >> 63 | v5 << 1 + v1 = v1 + v6 + W[row[11]] + vC = vC ~ v1 + vC = vC >> 32 | vC << 32 + vB = vB + vC + v6 = v6 ~ vB + v6 = v6 >> 24 | v6 << 40 + v1 = v1 + v6 + W[row[12]] + vC = vC ~ v1 + vC = vC >> 16 | vC << 48 + vB = vB + vC + v6 = v6 ~ vB + v6 = v6 >> 63 | v6 << 1 + v2 = v2 + v7 + W[row[13]] + vD = vD ~ v2 + vD = vD >> 32 | vD << 32 + v8 = v8 + vD + v7 = v7 ~ v8 + v7 = v7 >> 24 | v7 << 40 + v2 = v2 + v7 + W[row[14]] + vD = vD ~ v2 + vD = vD >> 16 | vD << 48 + v8 = v8 + vD + v7 = v7 ~ v8 + v7 = v7 >> 63 | v7 << 1 + v3 = v3 + v4 + W[row[15]] + vE = vE ~ v3 + vE = vE >> 32 | vE << 32 + v9 = v9 + vE + v4 = v4 ~ v9 + v4 = v4 >> 24 | v4 << 40 + v3 = v3 + v4 + W[row[16]] + vE = vE ~ v3 + vE = vE >> 16 | vE << 48 + v9 = v9 + vE + v4 = v4 ~ v9 + v4 = v4 >> 63 | v4 << 1 + end + h1 = h1 ~ v0 ~ v8 + h2 = h2 ~ v1 ~ v9 + h3 = h3 ~ v2 ~ vA + h4 = h4 ~ v3 ~ vB + h5 = h5 ~ v4 ~ vC + h6 = h6 ~ v5 ~ vD + h7 = h7 ~ v6 ~ vE + h8 = h8 ~ v7 ~ vF + end + H[1], H[2], H[3], H[4], H[5], H[6], H[7], H[8] = h1, h2, h3, h4, h5, h6, h7, h8 + return bytes_compressed + end + + local function blake3_feed_64(str, offs, size, flags, chunk_index, H_in, H_out, wide_output, block_length) + -- offs >= 0, size >= 0, size is multiple of 64 + block_length = block_length or 64 + local W = common_W + local h1, h2, h3, h4, h5, h6, h7, h8 = H_in[1], H_in[2], H_in[3], H_in[4], H_in[5], H_in[6], H_in[7], H_in[8] + H_out = H_out or H_in + for pos = offs + 1, offs + size, 64 do + if str then + W[1], W[2], W[3], W[4], W[5], W[6], W[7], W[8], W[9], W[10], W[11], W[12], W[13], W[14], W[15], W[16] = + string_unpack("> 16 | vC << 16 + v8 = v8 + vC + v4 = v4 ~ v8 + v4 = (v4 & (1<<32)-1) >> 12 | v4 << 20 + v0 = v0 + v4 + W[perm_blake3[j + 14]] + vC = vC ~ v0 + vC = (vC & (1<<32)-1) >> 8 | vC << 24 + v8 = v8 + vC + v4 = v4 ~ v8 + v4 = (v4 & (1<<32)-1) >> 7 | v4 << 25 + v1 = v1 + v5 + W[perm_blake3[j + 1]] + vD = vD ~ v1 + vD = (vD & (1<<32)-1) >> 16 | vD << 16 + v9 = v9 + vD + v5 = v5 ~ v9 + v5 = (v5 & (1<<32)-1) >> 12 | v5 << 20 + v1 = v1 + v5 + W[perm_blake3[j + 2]] + vD = vD ~ v1 + vD = (vD & (1<<32)-1) >> 8 | vD << 24 + v9 = v9 + vD + v5 = v5 ~ v9 + v5 = (v5 & (1<<32)-1) >> 7 | v5 << 25 + v2 = v2 + v6 + W[perm_blake3[j + 16]] + vE = vE ~ v2 + vE = (vE & (1<<32)-1) >> 16 | vE << 16 + vA = vA + vE + v6 = v6 ~ vA + v6 = (v6 & (1<<32)-1) >> 12 | v6 << 20 + v2 = v2 + v6 + W[perm_blake3[j + 7]] + vE = vE ~ v2 + vE = (vE & (1<<32)-1) >> 8 | vE << 24 + vA = vA + vE + v6 = v6 ~ vA + v6 = (v6 & (1<<32)-1) >> 7 | v6 << 25 + v3 = v3 + v7 + W[perm_blake3[j + 15]] + vF = vF ~ v3 + vF = (vF & (1<<32)-1) >> 16 | vF << 16 + vB = vB + vF + v7 = v7 ~ vB + v7 = (v7 & (1<<32)-1) >> 12 | v7 << 20 + v3 = v3 + v7 + W[perm_blake3[j + 17]] + vF = vF ~ v3 + vF = (vF & (1<<32)-1) >> 8 | vF << 24 + vB = vB + vF + v7 = v7 ~ vB + v7 = (v7 & (1<<32)-1) >> 7 | v7 << 25 + v0 = v0 + v5 + W[perm_blake3[j + 21]] + vF = vF ~ v0 + vF = (vF & (1<<32)-1) >> 16 | vF << 16 + vA = vA + vF + v5 = v5 ~ vA + v5 = (v5 & (1<<32)-1) >> 12 | v5 << 20 + v0 = v0 + v5 + W[perm_blake3[j + 5]] + vF = vF ~ v0 + vF = (vF & (1<<32)-1) >> 8 | vF << 24 + vA = vA + vF + v5 = v5 ~ vA + v5 = (v5 & (1<<32)-1) >> 7 | v5 << 25 + v1 = v1 + v6 + W[perm_blake3[j + 3]] + vC = vC ~ v1 + vC = (vC & (1<<32)-1) >> 16 | vC << 16 + vB = vB + vC + v6 = v6 ~ vB + v6 = (v6 & (1<<32)-1) >> 12 | v6 << 20 + v1 = v1 + v6 + W[perm_blake3[j + 6]] + vC = vC ~ v1 + vC = (vC & (1<<32)-1) >> 8 | vC << 24 + vB = vB + vC + v6 = v6 ~ vB + v6 = (v6 & (1<<32)-1) >> 7 | v6 << 25 + v2 = v2 + v7 + W[perm_blake3[j + 4]] + vD = vD ~ v2 + vD = (vD & (1<<32)-1) >> 16 | vD << 16 + v8 = v8 + vD + v7 = v7 ~ v8 + v7 = (v7 & (1<<32)-1) >> 12 | v7 << 20 + v2 = v2 + v7 + W[perm_blake3[j + 18]] + vD = vD ~ v2 + vD = (vD & (1<<32)-1) >> 8 | vD << 24 + v8 = v8 + vD + v7 = v7 ~ v8 + v7 = (v7 & (1<<32)-1) >> 7 | v7 << 25 + v3 = v3 + v4 + W[perm_blake3[j + 19]] + vE = vE ~ v3 + vE = (vE & (1<<32)-1) >> 16 | vE << 16 + v9 = v9 + vE + v4 = v4 ~ v9 + v4 = (v4 & (1<<32)-1) >> 12 | v4 << 20 + v3 = v3 + v4 + W[perm_blake3[j + 20]] + vE = vE ~ v3 + vE = (vE & (1<<32)-1) >> 8 | vE << 24 + v9 = v9 + vE + v4 = v4 ~ v9 + v4 = (v4 & (1<<32)-1) >> 7 | v4 << 25 + end + if wide_output then + H_out[ 9] = h1 ~ v8 + H_out[10] = h2 ~ v9 + H_out[11] = h3 ~ vA + H_out[12] = h4 ~ vB + H_out[13] = h5 ~ vC + H_out[14] = h6 ~ vD + H_out[15] = h7 ~ vE + H_out[16] = h8 ~ vF + end + h1 = v0 ~ v8 + h2 = v1 ~ v9 + h3 = v2 ~ vA + h4 = v3 ~ vB + h5 = v4 ~ vC + h6 = v5 ~ vD + h7 = v6 ~ vE + h8 = v7 ~ vF + end + H_out[1], H_out[2], H_out[3], H_out[4], H_out[5], H_out[6], H_out[7], H_out[8] = h1, h2, h3, h4, h5, h6, h7, h8 + end + + return HEX64, XORA5, XOR_BYTE, sha256_feed_64, sha512_feed_128, md5_feed_64, sha1_feed_64, keccak_feed, blake2s_feed_64, blake2b_feed_128, blake3_feed_64 + ]=](md5_next_shift, md5_K, sha2_K_lo, sha2_K_hi, build_keccak_format, sha3_RC_lo, sigma, common_W, sha2_H_lo, sha2_H_hi, perm_blake3) + +end + + +if branch == "INT32" then + + + -- implementation for Lua 5.3/5.4 having non-standard numbers config "int32"+"double" (built with LUA_INT_TYPE=LUA_INT_INT) + + K_lo_modulo = 2^32 + + function HEX(x) -- returns string of 8 lowercase hexadecimal digits + return string_format("%08x", x) + end + + XORA5, XOR_BYTE, sha256_feed_64, sha512_feed_128, md5_feed_64, sha1_feed_64, keccak_feed, blake2s_feed_64, blake2b_feed_128, blake3_feed_64 = load[=[-- branch "INT32" + local md5_next_shift, md5_K, sha2_K_lo, sha2_K_hi, build_keccak_format, sha3_RC_lo, sha3_RC_hi, sigma, common_W, sha2_H_lo, sha2_H_hi, perm_blake3 = ... + local string_unpack, floor = string.unpack, math.floor + + local function XORA5(x, y) + return x ~ (y and (y + 2^31) % 2^32 - 2^31 or 0xA5A5A5A5) + end + + local function XOR_BYTE(x, y) + return x ~ y + end + + local function sha256_feed_64(H, str, offs, size) + -- offs >= 0, size >= 0, size is multiple of 64 + local W, K = common_W, sha2_K_hi + local h1, h2, h3, h4, h5, h6, h7, h8 = H[1], H[2], H[3], H[4], H[5], H[6], H[7], H[8] + for pos = offs + 1, offs + size, 64 do + W[1], W[2], W[3], W[4], W[5], W[6], W[7], W[8], W[9], W[10], W[11], W[12], W[13], W[14], W[15], W[16] = + string_unpack(">i4i4i4i4i4i4i4i4i4i4i4i4i4i4i4i4", str, pos) + for j = 17, 64 do + local a, b = W[j-15], W[j-2] + W[j] = (a>>7 ~ a<<25 ~ a<<14 ~ a>>18 ~ a>>3) + (b<<15 ~ b>>17 ~ b<<13 ~ b>>19 ~ b>>10) + W[j-7] + W[j-16] + end + local a, b, c, d, e, f, g, h = h1, h2, h3, h4, h5, h6, h7, h8 + for j = 1, 64 do + local z = (e>>6 ~ e<<26 ~ e>>11 ~ e<<21 ~ e>>25 ~ e<<7) + (g ~ e & (f ~ g)) + h + K[j] + W[j] + h = g + g = f + f = e + e = z + d + d = c + c = b + b = a + a = z + ((a ~ c) & d ~ a & c) + (a>>2 ~ a<<30 ~ a>>13 ~ a<<19 ~ a<<10 ~ a>>22) + end + h1 = a + h1 + h2 = b + h2 + h3 = c + h3 + h4 = d + h4 + h5 = e + h5 + h6 = f + h6 + h7 = g + h7 + h8 = h + h8 + end + H[1], H[2], H[3], H[4], H[5], H[6], H[7], H[8] = h1, h2, h3, h4, h5, h6, h7, h8 + end + + local function sha512_feed_128(H_lo, H_hi, str, offs, size) + -- offs >= 0, size >= 0, size is multiple of 128 + -- W1_hi, W1_lo, W2_hi, W2_lo, ... Wk_hi = W[2*k-1], Wk_lo = W[2*k] + local floor, W, K_lo, K_hi = floor, common_W, sha2_K_lo, sha2_K_hi + local h1_lo, h2_lo, h3_lo, h4_lo, h5_lo, h6_lo, h7_lo, h8_lo = H_lo[1], H_lo[2], H_lo[3], H_lo[4], H_lo[5], H_lo[6], H_lo[7], H_lo[8] + local h1_hi, h2_hi, h3_hi, h4_hi, h5_hi, h6_hi, h7_hi, h8_hi = H_hi[1], H_hi[2], H_hi[3], H_hi[4], H_hi[5], H_hi[6], H_hi[7], H_hi[8] + for pos = offs + 1, offs + size, 128 do + W[1], W[2], W[3], W[4], W[5], W[6], W[7], W[8], W[9], W[10], W[11], W[12], W[13], W[14], W[15], W[16], + W[17], W[18], W[19], W[20], W[21], W[22], W[23], W[24], W[25], W[26], W[27], W[28], W[29], W[30], W[31], W[32] = + string_unpack(">i4i4i4i4i4i4i4i4i4i4i4i4i4i4i4i4i4i4i4i4i4i4i4i4i4i4i4i4i4i4i4i4", str, pos) + for jj = 17*2, 80*2, 2 do + local a_lo, a_hi, b_lo, b_hi = W[jj-30], W[jj-31], W[jj-4], W[jj-5] + local tmp = + (a_lo>>1 ~ a_hi<<31 ~ a_lo>>8 ~ a_hi<<24 ~ a_lo>>7 ~ a_hi<<25) % 2^32 + + (b_lo>>19 ~ b_hi<<13 ~ b_lo<<3 ~ b_hi>>29 ~ b_lo>>6 ~ b_hi<<26) % 2^32 + + W[jj-14] % 2^32 + W[jj-32] % 2^32 + W[jj-1] = + (a_hi>>1 ~ a_lo<<31 ~ a_hi>>8 ~ a_lo<<24 ~ a_hi>>7) + + (b_hi>>19 ~ b_lo<<13 ~ b_hi<<3 ~ b_lo>>29 ~ b_hi>>6) + + W[jj-15] + W[jj-33] + floor(tmp / 2^32) + W[jj] = 0|((tmp + 2^31) % 2^32 - 2^31) + end + local a_lo, b_lo, c_lo, d_lo, e_lo, f_lo, g_lo, h_lo = h1_lo, h2_lo, h3_lo, h4_lo, h5_lo, h6_lo, h7_lo, h8_lo + local a_hi, b_hi, c_hi, d_hi, e_hi, f_hi, g_hi, h_hi = h1_hi, h2_hi, h3_hi, h4_hi, h5_hi, h6_hi, h7_hi, h8_hi + for j = 1, 80 do + local jj = 2*j + local z_lo = (e_lo>>14 ~ e_hi<<18 ~ e_lo>>18 ~ e_hi<<14 ~ e_lo<<23 ~ e_hi>>9) % 2^32 + (g_lo ~ e_lo & (f_lo ~ g_lo)) % 2^32 + h_lo % 2^32 + K_lo[j] + W[jj] % 2^32 + local z_hi = (e_hi>>14 ~ e_lo<<18 ~ e_hi>>18 ~ e_lo<<14 ~ e_hi<<23 ~ e_lo>>9) + (g_hi ~ e_hi & (f_hi ~ g_hi)) + h_hi + K_hi[j] + W[jj-1] + floor(z_lo / 2^32) + z_lo = z_lo % 2^32 + h_lo = g_lo; h_hi = g_hi + g_lo = f_lo; g_hi = f_hi + f_lo = e_lo; f_hi = e_hi + e_lo = z_lo + d_lo % 2^32 + e_hi = z_hi + d_hi + floor(e_lo / 2^32) + e_lo = 0|((e_lo + 2^31) % 2^32 - 2^31) + d_lo = c_lo; d_hi = c_hi + c_lo = b_lo; c_hi = b_hi + b_lo = a_lo; b_hi = a_hi + z_lo = z_lo + (d_lo & c_lo ~ b_lo & (d_lo ~ c_lo)) % 2^32 + (b_lo>>28 ~ b_hi<<4 ~ b_lo<<30 ~ b_hi>>2 ~ b_lo<<25 ~ b_hi>>7) % 2^32 + a_hi = z_hi + (d_hi & c_hi ~ b_hi & (d_hi ~ c_hi)) + (b_hi>>28 ~ b_lo<<4 ~ b_hi<<30 ~ b_lo>>2 ~ b_hi<<25 ~ b_lo>>7) + floor(z_lo / 2^32) + a_lo = 0|((z_lo + 2^31) % 2^32 - 2^31) + end + a_lo = h1_lo % 2^32 + a_lo % 2^32 + h1_hi = h1_hi + a_hi + floor(a_lo / 2^32) + h1_lo = 0|((a_lo + 2^31) % 2^32 - 2^31) + a_lo = h2_lo % 2^32 + b_lo % 2^32 + h2_hi = h2_hi + b_hi + floor(a_lo / 2^32) + h2_lo = 0|((a_lo + 2^31) % 2^32 - 2^31) + a_lo = h3_lo % 2^32 + c_lo % 2^32 + h3_hi = h3_hi + c_hi + floor(a_lo / 2^32) + h3_lo = 0|((a_lo + 2^31) % 2^32 - 2^31) + a_lo = h4_lo % 2^32 + d_lo % 2^32 + h4_hi = h4_hi + d_hi + floor(a_lo / 2^32) + h4_lo = 0|((a_lo + 2^31) % 2^32 - 2^31) + a_lo = h5_lo % 2^32 + e_lo % 2^32 + h5_hi = h5_hi + e_hi + floor(a_lo / 2^32) + h5_lo = 0|((a_lo + 2^31) % 2^32 - 2^31) + a_lo = h6_lo % 2^32 + f_lo % 2^32 + h6_hi = h6_hi + f_hi + floor(a_lo / 2^32) + h6_lo = 0|((a_lo + 2^31) % 2^32 - 2^31) + a_lo = h7_lo % 2^32 + g_lo % 2^32 + h7_hi = h7_hi + g_hi + floor(a_lo / 2^32) + h7_lo = 0|((a_lo + 2^31) % 2^32 - 2^31) + a_lo = h8_lo % 2^32 + h_lo % 2^32 + h8_hi = h8_hi + h_hi + floor(a_lo / 2^32) + h8_lo = 0|((a_lo + 2^31) % 2^32 - 2^31) + end + H_lo[1], H_lo[2], H_lo[3], H_lo[4], H_lo[5], H_lo[6], H_lo[7], H_lo[8] = h1_lo, h2_lo, h3_lo, h4_lo, h5_lo, h6_lo, h7_lo, h8_lo + H_hi[1], H_hi[2], H_hi[3], H_hi[4], H_hi[5], H_hi[6], H_hi[7], H_hi[8] = h1_hi, h2_hi, h3_hi, h4_hi, h5_hi, h6_hi, h7_hi, h8_hi + end + + local function md5_feed_64(H, str, offs, size) + -- offs >= 0, size >= 0, size is multiple of 64 + local W, K, md5_next_shift = common_W, md5_K, md5_next_shift + local h1, h2, h3, h4 = H[1], H[2], H[3], H[4] + for pos = offs + 1, offs + size, 64 do + W[1], W[2], W[3], W[4], W[5], W[6], W[7], W[8], W[9], W[10], W[11], W[12], W[13], W[14], W[15], W[16] = + string_unpack(">s) + b + s = md5_next_shift[s] + end + s = 32-5 + for j = 17, 32 do + local F = (c ~ d & (b ~ c)) + a + K[j] + W[(5*j-4 & 15) + 1] + a = d + d = c + c = b + b = (F << 32-s | F>>s) + b + s = md5_next_shift[s] + end + s = 32-4 + for j = 33, 48 do + local F = (b ~ c ~ d) + a + K[j] + W[(3*j+2 & 15) + 1] + a = d + d = c + c = b + b = (F << 32-s | F>>s) + b + s = md5_next_shift[s] + end + s = 32-6 + for j = 49, 64 do + local F = (c ~ (b | ~d)) + a + K[j] + W[(j*7-7 & 15) + 1] + a = d + d = c + c = b + b = (F << 32-s | F>>s) + b + s = md5_next_shift[s] + end + h1 = a + h1 + h2 = b + h2 + h3 = c + h3 + h4 = d + h4 + end + H[1], H[2], H[3], H[4] = h1, h2, h3, h4 + end + + local function sha1_feed_64(H, str, offs, size) + -- offs >= 0, size >= 0, size is multiple of 64 + local W = common_W + local h1, h2, h3, h4, h5 = H[1], H[2], H[3], H[4], H[5] + for pos = offs + 1, offs + size, 64 do + W[1], W[2], W[3], W[4], W[5], W[6], W[7], W[8], W[9], W[10], W[11], W[12], W[13], W[14], W[15], W[16] = + string_unpack(">i4i4i4i4i4i4i4i4i4i4i4i4i4i4i4i4", str, pos) + for j = 17, 80 do + local a = W[j-3] ~ W[j-8] ~ W[j-14] ~ W[j-16] + W[j] = a << 1 ~ a >> 31 + end + local a, b, c, d, e = h1, h2, h3, h4, h5 + for j = 1, 20 do + local z = (a << 5 ~ a >> 27) + (d ~ b & (c ~ d)) + 0x5A827999 + W[j] + e -- constant = floor(2^30 * sqrt(2)) + e = d + d = c + c = b << 30 ~ b >> 2 + b = a + a = z + end + for j = 21, 40 do + local z = (a << 5 ~ a >> 27) + (b ~ c ~ d) + 0x6ED9EBA1 + W[j] + e -- 2^30 * sqrt(3) + e = d + d = c + c = b << 30 ~ b >> 2 + b = a + a = z + end + for j = 41, 60 do + local z = (a << 5 ~ a >> 27) + ((b ~ c) & d ~ b & c) + 0x8F1BBCDC + W[j] + e -- 2^30 * sqrt(5) + e = d + d = c + c = b << 30 ~ b >> 2 + b = a + a = z + end + for j = 61, 80 do + local z = (a << 5 ~ a >> 27) + (b ~ c ~ d) + 0xCA62C1D6 + W[j] + e -- 2^30 * sqrt(10) + e = d + d = c + c = b << 30 ~ b >> 2 + b = a + a = z + end + h1 = a + h1 + h2 = b + h2 + h3 = c + h3 + h4 = d + h4 + h5 = e + h5 + end + H[1], H[2], H[3], H[4], H[5] = h1, h2, h3, h4, h5 + end + + local keccak_format_i4i4 = build_keccak_format("i4i4") + + local function keccak_feed(lanes_lo, lanes_hi, str, offs, size, block_size_in_bytes) + -- offs >= 0, size >= 0, size is multiple of block_size_in_bytes, block_size_in_bytes is positive multiple of 8 + local RC_lo, RC_hi = sha3_RC_lo, sha3_RC_hi + local qwords_qty = block_size_in_bytes / 8 + local keccak_format = keccak_format_i4i4[qwords_qty] + for pos = offs + 1, offs + size, block_size_in_bytes do + local dwords_from_message = {string_unpack(keccak_format, str, pos)} + for j = 1, qwords_qty do + lanes_lo[j] = lanes_lo[j] ~ dwords_from_message[2*j-1] + lanes_hi[j] = lanes_hi[j] ~ dwords_from_message[2*j] + end + local L01_lo, L01_hi, L02_lo, L02_hi, L03_lo, L03_hi, L04_lo, L04_hi, L05_lo, L05_hi, L06_lo, L06_hi, L07_lo, L07_hi, L08_lo, L08_hi, + L09_lo, L09_hi, L10_lo, L10_hi, L11_lo, L11_hi, L12_lo, L12_hi, L13_lo, L13_hi, L14_lo, L14_hi, L15_lo, L15_hi, L16_lo, L16_hi, + L17_lo, L17_hi, L18_lo, L18_hi, L19_lo, L19_hi, L20_lo, L20_hi, L21_lo, L21_hi, L22_lo, L22_hi, L23_lo, L23_hi, L24_lo, L24_hi, L25_lo, L25_hi = + lanes_lo[1], lanes_hi[1], lanes_lo[2], lanes_hi[2], lanes_lo[3], lanes_hi[3], lanes_lo[4], lanes_hi[4], lanes_lo[5], lanes_hi[5], + lanes_lo[6], lanes_hi[6], lanes_lo[7], lanes_hi[7], lanes_lo[8], lanes_hi[8], lanes_lo[9], lanes_hi[9], lanes_lo[10], lanes_hi[10], + lanes_lo[11], lanes_hi[11], lanes_lo[12], lanes_hi[12], lanes_lo[13], lanes_hi[13], lanes_lo[14], lanes_hi[14], lanes_lo[15], lanes_hi[15], + lanes_lo[16], lanes_hi[16], lanes_lo[17], lanes_hi[17], lanes_lo[18], lanes_hi[18], lanes_lo[19], lanes_hi[19], lanes_lo[20], lanes_hi[20], + lanes_lo[21], lanes_hi[21], lanes_lo[22], lanes_hi[22], lanes_lo[23], lanes_hi[23], lanes_lo[24], lanes_hi[24], lanes_lo[25], lanes_hi[25] + for round_idx = 1, 24 do + local C1_lo = L01_lo ~ L06_lo ~ L11_lo ~ L16_lo ~ L21_lo + local C1_hi = L01_hi ~ L06_hi ~ L11_hi ~ L16_hi ~ L21_hi + local C2_lo = L02_lo ~ L07_lo ~ L12_lo ~ L17_lo ~ L22_lo + local C2_hi = L02_hi ~ L07_hi ~ L12_hi ~ L17_hi ~ L22_hi + local C3_lo = L03_lo ~ L08_lo ~ L13_lo ~ L18_lo ~ L23_lo + local C3_hi = L03_hi ~ L08_hi ~ L13_hi ~ L18_hi ~ L23_hi + local C4_lo = L04_lo ~ L09_lo ~ L14_lo ~ L19_lo ~ L24_lo + local C4_hi = L04_hi ~ L09_hi ~ L14_hi ~ L19_hi ~ L24_hi + local C5_lo = L05_lo ~ L10_lo ~ L15_lo ~ L20_lo ~ L25_lo + local C5_hi = L05_hi ~ L10_hi ~ L15_hi ~ L20_hi ~ L25_hi + local D_lo = C1_lo ~ C3_lo<<1 ~ C3_hi>>31 + local D_hi = C1_hi ~ C3_hi<<1 ~ C3_lo>>31 + local T0_lo = D_lo ~ L02_lo + local T0_hi = D_hi ~ L02_hi + local T1_lo = D_lo ~ L07_lo + local T1_hi = D_hi ~ L07_hi + local T2_lo = D_lo ~ L12_lo + local T2_hi = D_hi ~ L12_hi + local T3_lo = D_lo ~ L17_lo + local T3_hi = D_hi ~ L17_hi + local T4_lo = D_lo ~ L22_lo + local T4_hi = D_hi ~ L22_hi + L02_lo = T1_lo>>20 ~ T1_hi<<12 + L02_hi = T1_hi>>20 ~ T1_lo<<12 + L07_lo = T3_lo>>19 ~ T3_hi<<13 + L07_hi = T3_hi>>19 ~ T3_lo<<13 + L12_lo = T0_lo<<1 ~ T0_hi>>31 + L12_hi = T0_hi<<1 ~ T0_lo>>31 + L17_lo = T2_lo<<10 ~ T2_hi>>22 + L17_hi = T2_hi<<10 ~ T2_lo>>22 + L22_lo = T4_lo<<2 ~ T4_hi>>30 + L22_hi = T4_hi<<2 ~ T4_lo>>30 + D_lo = C2_lo ~ C4_lo<<1 ~ C4_hi>>31 + D_hi = C2_hi ~ C4_hi<<1 ~ C4_lo>>31 + T0_lo = D_lo ~ L03_lo + T0_hi = D_hi ~ L03_hi + T1_lo = D_lo ~ L08_lo + T1_hi = D_hi ~ L08_hi + T2_lo = D_lo ~ L13_lo + T2_hi = D_hi ~ L13_hi + T3_lo = D_lo ~ L18_lo + T3_hi = D_hi ~ L18_hi + T4_lo = D_lo ~ L23_lo + T4_hi = D_hi ~ L23_hi + L03_lo = T2_lo>>21 ~ T2_hi<<11 + L03_hi = T2_hi>>21 ~ T2_lo<<11 + L08_lo = T4_lo>>3 ~ T4_hi<<29 + L08_hi = T4_hi>>3 ~ T4_lo<<29 + L13_lo = T1_lo<<6 ~ T1_hi>>26 + L13_hi = T1_hi<<6 ~ T1_lo>>26 + L18_lo = T3_lo<<15 ~ T3_hi>>17 + L18_hi = T3_hi<<15 ~ T3_lo>>17 + L23_lo = T0_lo>>2 ~ T0_hi<<30 + L23_hi = T0_hi>>2 ~ T0_lo<<30 + D_lo = C3_lo ~ C5_lo<<1 ~ C5_hi>>31 + D_hi = C3_hi ~ C5_hi<<1 ~ C5_lo>>31 + T0_lo = D_lo ~ L04_lo + T0_hi = D_hi ~ L04_hi + T1_lo = D_lo ~ L09_lo + T1_hi = D_hi ~ L09_hi + T2_lo = D_lo ~ L14_lo + T2_hi = D_hi ~ L14_hi + T3_lo = D_lo ~ L19_lo + T3_hi = D_hi ~ L19_hi + T4_lo = D_lo ~ L24_lo + T4_hi = D_hi ~ L24_hi + L04_lo = T3_lo<<21 ~ T3_hi>>11 + L04_hi = T3_hi<<21 ~ T3_lo>>11 + L09_lo = T0_lo<<28 ~ T0_hi>>4 + L09_hi = T0_hi<<28 ~ T0_lo>>4 + L14_lo = T2_lo<<25 ~ T2_hi>>7 + L14_hi = T2_hi<<25 ~ T2_lo>>7 + L19_lo = T4_lo>>8 ~ T4_hi<<24 + L19_hi = T4_hi>>8 ~ T4_lo<<24 + L24_lo = T1_lo>>9 ~ T1_hi<<23 + L24_hi = T1_hi>>9 ~ T1_lo<<23 + D_lo = C4_lo ~ C1_lo<<1 ~ C1_hi>>31 + D_hi = C4_hi ~ C1_hi<<1 ~ C1_lo>>31 + T0_lo = D_lo ~ L05_lo + T0_hi = D_hi ~ L05_hi + T1_lo = D_lo ~ L10_lo + T1_hi = D_hi ~ L10_hi + T2_lo = D_lo ~ L15_lo + T2_hi = D_hi ~ L15_hi + T3_lo = D_lo ~ L20_lo + T3_hi = D_hi ~ L20_hi + T4_lo = D_lo ~ L25_lo + T4_hi = D_hi ~ L25_hi + L05_lo = T4_lo<<14 ~ T4_hi>>18 + L05_hi = T4_hi<<14 ~ T4_lo>>18 + L10_lo = T1_lo<<20 ~ T1_hi>>12 + L10_hi = T1_hi<<20 ~ T1_lo>>12 + L15_lo = T3_lo<<8 ~ T3_hi>>24 + L15_hi = T3_hi<<8 ~ T3_lo>>24 + L20_lo = T0_lo<<27 ~ T0_hi>>5 + L20_hi = T0_hi<<27 ~ T0_lo>>5 + L25_lo = T2_lo>>25 ~ T2_hi<<7 + L25_hi = T2_hi>>25 ~ T2_lo<<7 + D_lo = C5_lo ~ C2_lo<<1 ~ C2_hi>>31 + D_hi = C5_hi ~ C2_hi<<1 ~ C2_lo>>31 + T1_lo = D_lo ~ L06_lo + T1_hi = D_hi ~ L06_hi + T2_lo = D_lo ~ L11_lo + T2_hi = D_hi ~ L11_hi + T3_lo = D_lo ~ L16_lo + T3_hi = D_hi ~ L16_hi + T4_lo = D_lo ~ L21_lo + T4_hi = D_hi ~ L21_hi + L06_lo = T2_lo<<3 ~ T2_hi>>29 + L06_hi = T2_hi<<3 ~ T2_lo>>29 + L11_lo = T4_lo<<18 ~ T4_hi>>14 + L11_hi = T4_hi<<18 ~ T4_lo>>14 + L16_lo = T1_lo>>28 ~ T1_hi<<4 + L16_hi = T1_hi>>28 ~ T1_lo<<4 + L21_lo = T3_lo>>23 ~ T3_hi<<9 + L21_hi = T3_hi>>23 ~ T3_lo<<9 + L01_lo = D_lo ~ L01_lo + L01_hi = D_hi ~ L01_hi + L01_lo, L02_lo, L03_lo, L04_lo, L05_lo = L01_lo ~ ~L02_lo & L03_lo, L02_lo ~ ~L03_lo & L04_lo, L03_lo ~ ~L04_lo & L05_lo, L04_lo ~ ~L05_lo & L01_lo, L05_lo ~ ~L01_lo & L02_lo + L01_hi, L02_hi, L03_hi, L04_hi, L05_hi = L01_hi ~ ~L02_hi & L03_hi, L02_hi ~ ~L03_hi & L04_hi, L03_hi ~ ~L04_hi & L05_hi, L04_hi ~ ~L05_hi & L01_hi, L05_hi ~ ~L01_hi & L02_hi + L06_lo, L07_lo, L08_lo, L09_lo, L10_lo = L09_lo ~ ~L10_lo & L06_lo, L10_lo ~ ~L06_lo & L07_lo, L06_lo ~ ~L07_lo & L08_lo, L07_lo ~ ~L08_lo & L09_lo, L08_lo ~ ~L09_lo & L10_lo + L06_hi, L07_hi, L08_hi, L09_hi, L10_hi = L09_hi ~ ~L10_hi & L06_hi, L10_hi ~ ~L06_hi & L07_hi, L06_hi ~ ~L07_hi & L08_hi, L07_hi ~ ~L08_hi & L09_hi, L08_hi ~ ~L09_hi & L10_hi + L11_lo, L12_lo, L13_lo, L14_lo, L15_lo = L12_lo ~ ~L13_lo & L14_lo, L13_lo ~ ~L14_lo & L15_lo, L14_lo ~ ~L15_lo & L11_lo, L15_lo ~ ~L11_lo & L12_lo, L11_lo ~ ~L12_lo & L13_lo + L11_hi, L12_hi, L13_hi, L14_hi, L15_hi = L12_hi ~ ~L13_hi & L14_hi, L13_hi ~ ~L14_hi & L15_hi, L14_hi ~ ~L15_hi & L11_hi, L15_hi ~ ~L11_hi & L12_hi, L11_hi ~ ~L12_hi & L13_hi + L16_lo, L17_lo, L18_lo, L19_lo, L20_lo = L20_lo ~ ~L16_lo & L17_lo, L16_lo ~ ~L17_lo & L18_lo, L17_lo ~ ~L18_lo & L19_lo, L18_lo ~ ~L19_lo & L20_lo, L19_lo ~ ~L20_lo & L16_lo + L16_hi, L17_hi, L18_hi, L19_hi, L20_hi = L20_hi ~ ~L16_hi & L17_hi, L16_hi ~ ~L17_hi & L18_hi, L17_hi ~ ~L18_hi & L19_hi, L18_hi ~ ~L19_hi & L20_hi, L19_hi ~ ~L20_hi & L16_hi + L21_lo, L22_lo, L23_lo, L24_lo, L25_lo = L23_lo ~ ~L24_lo & L25_lo, L24_lo ~ ~L25_lo & L21_lo, L25_lo ~ ~L21_lo & L22_lo, L21_lo ~ ~L22_lo & L23_lo, L22_lo ~ ~L23_lo & L24_lo + L21_hi, L22_hi, L23_hi, L24_hi, L25_hi = L23_hi ~ ~L24_hi & L25_hi, L24_hi ~ ~L25_hi & L21_hi, L25_hi ~ ~L21_hi & L22_hi, L21_hi ~ ~L22_hi & L23_hi, L22_hi ~ ~L23_hi & L24_hi + L01_lo = L01_lo ~ RC_lo[round_idx] + L01_hi = L01_hi ~ RC_hi[round_idx] + end + lanes_lo[1] = L01_lo; lanes_hi[1] = L01_hi + lanes_lo[2] = L02_lo; lanes_hi[2] = L02_hi + lanes_lo[3] = L03_lo; lanes_hi[3] = L03_hi + lanes_lo[4] = L04_lo; lanes_hi[4] = L04_hi + lanes_lo[5] = L05_lo; lanes_hi[5] = L05_hi + lanes_lo[6] = L06_lo; lanes_hi[6] = L06_hi + lanes_lo[7] = L07_lo; lanes_hi[7] = L07_hi + lanes_lo[8] = L08_lo; lanes_hi[8] = L08_hi + lanes_lo[9] = L09_lo; lanes_hi[9] = L09_hi + lanes_lo[10] = L10_lo; lanes_hi[10] = L10_hi + lanes_lo[11] = L11_lo; lanes_hi[11] = L11_hi + lanes_lo[12] = L12_lo; lanes_hi[12] = L12_hi + lanes_lo[13] = L13_lo; lanes_hi[13] = L13_hi + lanes_lo[14] = L14_lo; lanes_hi[14] = L14_hi + lanes_lo[15] = L15_lo; lanes_hi[15] = L15_hi + lanes_lo[16] = L16_lo; lanes_hi[16] = L16_hi + lanes_lo[17] = L17_lo; lanes_hi[17] = L17_hi + lanes_lo[18] = L18_lo; lanes_hi[18] = L18_hi + lanes_lo[19] = L19_lo; lanes_hi[19] = L19_hi + lanes_lo[20] = L20_lo; lanes_hi[20] = L20_hi + lanes_lo[21] = L21_lo; lanes_hi[21] = L21_hi + lanes_lo[22] = L22_lo; lanes_hi[22] = L22_hi + lanes_lo[23] = L23_lo; lanes_hi[23] = L23_hi + lanes_lo[24] = L24_lo; lanes_hi[24] = L24_hi + lanes_lo[25] = L25_lo; lanes_hi[25] = L25_hi + end + end + + local function blake2s_feed_64(H, str, offs, size, bytes_compressed, last_block_size, is_last_node) + -- offs >= 0, size >= 0, size is multiple of 64 + local W = common_W + local h1, h2, h3, h4, h5, h6, h7, h8 = H[1], H[2], H[3], H[4], H[5], H[6], H[7], H[8] + for pos = offs + 1, offs + size, 64 do + if str then + W[1], W[2], W[3], W[4], W[5], W[6], W[7], W[8], W[9], W[10], W[11], W[12], W[13], W[14], W[15], W[16] = + string_unpack("> 16 | vC << 16 + v8 = v8 + vC + v4 = v4 ~ v8 + v4 = v4 >> 12 | v4 << 20 + v0 = v0 + v4 + W[row[2]] + vC = vC ~ v0 + vC = vC >> 8 | vC << 24 + v8 = v8 + vC + v4 = v4 ~ v8 + v4 = v4 >> 7 | v4 << 25 + v1 = v1 + v5 + W[row[3]] + vD = vD ~ v1 + vD = vD >> 16 | vD << 16 + v9 = v9 + vD + v5 = v5 ~ v9 + v5 = v5 >> 12 | v5 << 20 + v1 = v1 + v5 + W[row[4]] + vD = vD ~ v1 + vD = vD >> 8 | vD << 24 + v9 = v9 + vD + v5 = v5 ~ v9 + v5 = v5 >> 7 | v5 << 25 + v2 = v2 + v6 + W[row[5]] + vE = vE ~ v2 + vE = vE >> 16 | vE << 16 + vA = vA + vE + v6 = v6 ~ vA + v6 = v6 >> 12 | v6 << 20 + v2 = v2 + v6 + W[row[6]] + vE = vE ~ v2 + vE = vE >> 8 | vE << 24 + vA = vA + vE + v6 = v6 ~ vA + v6 = v6 >> 7 | v6 << 25 + v3 = v3 + v7 + W[row[7]] + vF = vF ~ v3 + vF = vF >> 16 | vF << 16 + vB = vB + vF + v7 = v7 ~ vB + v7 = v7 >> 12 | v7 << 20 + v3 = v3 + v7 + W[row[8]] + vF = vF ~ v3 + vF = vF >> 8 | vF << 24 + vB = vB + vF + v7 = v7 ~ vB + v7 = v7 >> 7 | v7 << 25 + v0 = v0 + v5 + W[row[9]] + vF = vF ~ v0 + vF = vF >> 16 | vF << 16 + vA = vA + vF + v5 = v5 ~ vA + v5 = v5 >> 12 | v5 << 20 + v0 = v0 + v5 + W[row[10]] + vF = vF ~ v0 + vF = vF >> 8 | vF << 24 + vA = vA + vF + v5 = v5 ~ vA + v5 = v5 >> 7 | v5 << 25 + v1 = v1 + v6 + W[row[11]] + vC = vC ~ v1 + vC = vC >> 16 | vC << 16 + vB = vB + vC + v6 = v6 ~ vB + v6 = v6 >> 12 | v6 << 20 + v1 = v1 + v6 + W[row[12]] + vC = vC ~ v1 + vC = vC >> 8 | vC << 24 + vB = vB + vC + v6 = v6 ~ vB + v6 = v6 >> 7 | v6 << 25 + v2 = v2 + v7 + W[row[13]] + vD = vD ~ v2 + vD = vD >> 16 | vD << 16 + v8 = v8 + vD + v7 = v7 ~ v8 + v7 = v7 >> 12 | v7 << 20 + v2 = v2 + v7 + W[row[14]] + vD = vD ~ v2 + vD = vD >> 8 | vD << 24 + v8 = v8 + vD + v7 = v7 ~ v8 + v7 = v7 >> 7 | v7 << 25 + v3 = v3 + v4 + W[row[15]] + vE = vE ~ v3 + vE = vE >> 16 | vE << 16 + v9 = v9 + vE + v4 = v4 ~ v9 + v4 = v4 >> 12 | v4 << 20 + v3 = v3 + v4 + W[row[16]] + vE = vE ~ v3 + vE = vE >> 8 | vE << 24 + v9 = v9 + vE + v4 = v4 ~ v9 + v4 = v4 >> 7 | v4 << 25 + end + h1 = h1 ~ v0 ~ v8 + h2 = h2 ~ v1 ~ v9 + h3 = h3 ~ v2 ~ vA + h4 = h4 ~ v3 ~ vB + h5 = h5 ~ v4 ~ vC + h6 = h6 ~ v5 ~ vD + h7 = h7 ~ v6 ~ vE + h8 = h8 ~ v7 ~ vF + end + H[1], H[2], H[3], H[4], H[5], H[6], H[7], H[8] = h1, h2, h3, h4, h5, h6, h7, h8 + return bytes_compressed + end + + local function blake2b_feed_128(H_lo, H_hi, str, offs, size, bytes_compressed, last_block_size, is_last_node) + -- offs >= 0, size >= 0, size is multiple of 128 + local W = common_W + local h1_lo, h2_lo, h3_lo, h4_lo, h5_lo, h6_lo, h7_lo, h8_lo = H_lo[1], H_lo[2], H_lo[3], H_lo[4], H_lo[5], H_lo[6], H_lo[7], H_lo[8] + local h1_hi, h2_hi, h3_hi, h4_hi, h5_hi, h6_hi, h7_hi, h8_hi = H_hi[1], H_hi[2], H_hi[3], H_hi[4], H_hi[5], H_hi[6], H_hi[7], H_hi[8] + for pos = offs + 1, offs + size, 128 do + if str then + W[1], W[2], W[3], W[4], W[5], W[6], W[7], W[8], W[9], W[10], W[11], W[12], W[13], W[14], W[15], W[16], + W[17], W[18], W[19], W[20], W[21], W[22], W[23], W[24], W[25], W[26], W[27], W[28], W[29], W[30], W[31], W[32] = + string_unpack("> 24 | v4_hi << 8, v4_hi >> 24 | v4_lo << 8 + k = row[2] * 2 + v0_lo = v0_lo % 2^32 + v4_lo % 2^32 + W[k-1] % 2^32 + v0_hi = v0_hi + v4_hi + floor(v0_lo / 2^32) + W[k] + v0_lo = 0|((v0_lo + 2^31) % 2^32 - 2^31) + vC_lo, vC_hi = vC_lo ~ v0_lo, vC_hi ~ v0_hi + vC_lo, vC_hi = vC_lo >> 16 | vC_hi << 16, vC_hi >> 16 | vC_lo << 16 + v8_lo = v8_lo % 2^32 + vC_lo % 2^32 + v8_hi = v8_hi + vC_hi + floor(v8_lo / 2^32) + v8_lo = 0|((v8_lo + 2^31) % 2^32 - 2^31) + v4_lo, v4_hi = v4_lo ~ v8_lo, v4_hi ~ v8_hi + v4_lo, v4_hi = v4_lo << 1 | v4_hi >> 31, v4_hi << 1 | v4_lo >> 31 + k = row[3] * 2 + v1_lo = v1_lo % 2^32 + v5_lo % 2^32 + W[k-1] % 2^32 + v1_hi = v1_hi + v5_hi + floor(v1_lo / 2^32) + W[k] + v1_lo = 0|((v1_lo + 2^31) % 2^32 - 2^31) + vD_lo, vD_hi = vD_hi ~ v1_hi, vD_lo ~ v1_lo + v9_lo = v9_lo % 2^32 + vD_lo % 2^32 + v9_hi = v9_hi + vD_hi + floor(v9_lo / 2^32) + v9_lo = 0|((v9_lo + 2^31) % 2^32 - 2^31) + v5_lo, v5_hi = v5_lo ~ v9_lo, v5_hi ~ v9_hi + v5_lo, v5_hi = v5_lo >> 24 | v5_hi << 8, v5_hi >> 24 | v5_lo << 8 + k = row[4] * 2 + v1_lo = v1_lo % 2^32 + v5_lo % 2^32 + W[k-1] % 2^32 + v1_hi = v1_hi + v5_hi + floor(v1_lo / 2^32) + W[k] + v1_lo = 0|((v1_lo + 2^31) % 2^32 - 2^31) + vD_lo, vD_hi = vD_lo ~ v1_lo, vD_hi ~ v1_hi + vD_lo, vD_hi = vD_lo >> 16 | vD_hi << 16, vD_hi >> 16 | vD_lo << 16 + v9_lo = v9_lo % 2^32 + vD_lo % 2^32 + v9_hi = v9_hi + vD_hi + floor(v9_lo / 2^32) + v9_lo = 0|((v9_lo + 2^31) % 2^32 - 2^31) + v5_lo, v5_hi = v5_lo ~ v9_lo, v5_hi ~ v9_hi + v5_lo, v5_hi = v5_lo << 1 | v5_hi >> 31, v5_hi << 1 | v5_lo >> 31 + k = row[5] * 2 + v2_lo = v2_lo % 2^32 + v6_lo % 2^32 + W[k-1] % 2^32 + v2_hi = v2_hi + v6_hi + floor(v2_lo / 2^32) + W[k] + v2_lo = 0|((v2_lo + 2^31) % 2^32 - 2^31) + vE_lo, vE_hi = vE_hi ~ v2_hi, vE_lo ~ v2_lo + vA_lo = vA_lo % 2^32 + vE_lo % 2^32 + vA_hi = vA_hi + vE_hi + floor(vA_lo / 2^32) + vA_lo = 0|((vA_lo + 2^31) % 2^32 - 2^31) + v6_lo, v6_hi = v6_lo ~ vA_lo, v6_hi ~ vA_hi + v6_lo, v6_hi = v6_lo >> 24 | v6_hi << 8, v6_hi >> 24 | v6_lo << 8 + k = row[6] * 2 + v2_lo = v2_lo % 2^32 + v6_lo % 2^32 + W[k-1] % 2^32 + v2_hi = v2_hi + v6_hi + floor(v2_lo / 2^32) + W[k] + v2_lo = 0|((v2_lo + 2^31) % 2^32 - 2^31) + vE_lo, vE_hi = vE_lo ~ v2_lo, vE_hi ~ v2_hi + vE_lo, vE_hi = vE_lo >> 16 | vE_hi << 16, vE_hi >> 16 | vE_lo << 16 + vA_lo = vA_lo % 2^32 + vE_lo % 2^32 + vA_hi = vA_hi + vE_hi + floor(vA_lo / 2^32) + vA_lo = 0|((vA_lo + 2^31) % 2^32 - 2^31) + v6_lo, v6_hi = v6_lo ~ vA_lo, v6_hi ~ vA_hi + v6_lo, v6_hi = v6_lo << 1 | v6_hi >> 31, v6_hi << 1 | v6_lo >> 31 + k = row[7] * 2 + v3_lo = v3_lo % 2^32 + v7_lo % 2^32 + W[k-1] % 2^32 + v3_hi = v3_hi + v7_hi + floor(v3_lo / 2^32) + W[k] + v3_lo = 0|((v3_lo + 2^31) % 2^32 - 2^31) + vF_lo, vF_hi = vF_hi ~ v3_hi, vF_lo ~ v3_lo + vB_lo = vB_lo % 2^32 + vF_lo % 2^32 + vB_hi = vB_hi + vF_hi + floor(vB_lo / 2^32) + vB_lo = 0|((vB_lo + 2^31) % 2^32 - 2^31) + v7_lo, v7_hi = v7_lo ~ vB_lo, v7_hi ~ vB_hi + v7_lo, v7_hi = v7_lo >> 24 | v7_hi << 8, v7_hi >> 24 | v7_lo << 8 + k = row[8] * 2 + v3_lo = v3_lo % 2^32 + v7_lo % 2^32 + W[k-1] % 2^32 + v3_hi = v3_hi + v7_hi + floor(v3_lo / 2^32) + W[k] + v3_lo = 0|((v3_lo + 2^31) % 2^32 - 2^31) + vF_lo, vF_hi = vF_lo ~ v3_lo, vF_hi ~ v3_hi + vF_lo, vF_hi = vF_lo >> 16 | vF_hi << 16, vF_hi >> 16 | vF_lo << 16 + vB_lo = vB_lo % 2^32 + vF_lo % 2^32 + vB_hi = vB_hi + vF_hi + floor(vB_lo / 2^32) + vB_lo = 0|((vB_lo + 2^31) % 2^32 - 2^31) + v7_lo, v7_hi = v7_lo ~ vB_lo, v7_hi ~ vB_hi + v7_lo, v7_hi = v7_lo << 1 | v7_hi >> 31, v7_hi << 1 | v7_lo >> 31 + k = row[9] * 2 + v0_lo = v0_lo % 2^32 + v5_lo % 2^32 + W[k-1] % 2^32 + v0_hi = v0_hi + v5_hi + floor(v0_lo / 2^32) + W[k] + v0_lo = 0|((v0_lo + 2^31) % 2^32 - 2^31) + vF_lo, vF_hi = vF_hi ~ v0_hi, vF_lo ~ v0_lo + vA_lo = vA_lo % 2^32 + vF_lo % 2^32 + vA_hi = vA_hi + vF_hi + floor(vA_lo / 2^32) + vA_lo = 0|((vA_lo + 2^31) % 2^32 - 2^31) + v5_lo, v5_hi = v5_lo ~ vA_lo, v5_hi ~ vA_hi + v5_lo, v5_hi = v5_lo >> 24 | v5_hi << 8, v5_hi >> 24 | v5_lo << 8 + k = row[10] * 2 + v0_lo = v0_lo % 2^32 + v5_lo % 2^32 + W[k-1] % 2^32 + v0_hi = v0_hi + v5_hi + floor(v0_lo / 2^32) + W[k] + v0_lo = 0|((v0_lo + 2^31) % 2^32 - 2^31) + vF_lo, vF_hi = vF_lo ~ v0_lo, vF_hi ~ v0_hi + vF_lo, vF_hi = vF_lo >> 16 | vF_hi << 16, vF_hi >> 16 | vF_lo << 16 + vA_lo = vA_lo % 2^32 + vF_lo % 2^32 + vA_hi = vA_hi + vF_hi + floor(vA_lo / 2^32) + vA_lo = 0|((vA_lo + 2^31) % 2^32 - 2^31) + v5_lo, v5_hi = v5_lo ~ vA_lo, v5_hi ~ vA_hi + v5_lo, v5_hi = v5_lo << 1 | v5_hi >> 31, v5_hi << 1 | v5_lo >> 31 + k = row[11] * 2 + v1_lo = v1_lo % 2^32 + v6_lo % 2^32 + W[k-1] % 2^32 + v1_hi = v1_hi + v6_hi + floor(v1_lo / 2^32) + W[k] + v1_lo = 0|((v1_lo + 2^31) % 2^32 - 2^31) + vC_lo, vC_hi = vC_hi ~ v1_hi, vC_lo ~ v1_lo + vB_lo = vB_lo % 2^32 + vC_lo % 2^32 + vB_hi = vB_hi + vC_hi + floor(vB_lo / 2^32) + vB_lo = 0|((vB_lo + 2^31) % 2^32 - 2^31) + v6_lo, v6_hi = v6_lo ~ vB_lo, v6_hi ~ vB_hi + v6_lo, v6_hi = v6_lo >> 24 | v6_hi << 8, v6_hi >> 24 | v6_lo << 8 + k = row[12] * 2 + v1_lo = v1_lo % 2^32 + v6_lo % 2^32 + W[k-1] % 2^32 + v1_hi = v1_hi + v6_hi + floor(v1_lo / 2^32) + W[k] + v1_lo = 0|((v1_lo + 2^31) % 2^32 - 2^31) + vC_lo, vC_hi = vC_lo ~ v1_lo, vC_hi ~ v1_hi + vC_lo, vC_hi = vC_lo >> 16 | vC_hi << 16, vC_hi >> 16 | vC_lo << 16 + vB_lo = vB_lo % 2^32 + vC_lo % 2^32 + vB_hi = vB_hi + vC_hi + floor(vB_lo / 2^32) + vB_lo = 0|((vB_lo + 2^31) % 2^32 - 2^31) + v6_lo, v6_hi = v6_lo ~ vB_lo, v6_hi ~ vB_hi + v6_lo, v6_hi = v6_lo << 1 | v6_hi >> 31, v6_hi << 1 | v6_lo >> 31 + k = row[13] * 2 + v2_lo = v2_lo % 2^32 + v7_lo % 2^32 + W[k-1] % 2^32 + v2_hi = v2_hi + v7_hi + floor(v2_lo / 2^32) + W[k] + v2_lo = 0|((v2_lo + 2^31) % 2^32 - 2^31) + vD_lo, vD_hi = vD_hi ~ v2_hi, vD_lo ~ v2_lo + v8_lo = v8_lo % 2^32 + vD_lo % 2^32 + v8_hi = v8_hi + vD_hi + floor(v8_lo / 2^32) + v8_lo = 0|((v8_lo + 2^31) % 2^32 - 2^31) + v7_lo, v7_hi = v7_lo ~ v8_lo, v7_hi ~ v8_hi + v7_lo, v7_hi = v7_lo >> 24 | v7_hi << 8, v7_hi >> 24 | v7_lo << 8 + k = row[14] * 2 + v2_lo = v2_lo % 2^32 + v7_lo % 2^32 + W[k-1] % 2^32 + v2_hi = v2_hi + v7_hi + floor(v2_lo / 2^32) + W[k] + v2_lo = 0|((v2_lo + 2^31) % 2^32 - 2^31) + vD_lo, vD_hi = vD_lo ~ v2_lo, vD_hi ~ v2_hi + vD_lo, vD_hi = vD_lo >> 16 | vD_hi << 16, vD_hi >> 16 | vD_lo << 16 + v8_lo = v8_lo % 2^32 + vD_lo % 2^32 + v8_hi = v8_hi + vD_hi + floor(v8_lo / 2^32) + v8_lo = 0|((v8_lo + 2^31) % 2^32 - 2^31) + v7_lo, v7_hi = v7_lo ~ v8_lo, v7_hi ~ v8_hi + v7_lo, v7_hi = v7_lo << 1 | v7_hi >> 31, v7_hi << 1 | v7_lo >> 31 + k = row[15] * 2 + v3_lo = v3_lo % 2^32 + v4_lo % 2^32 + W[k-1] % 2^32 + v3_hi = v3_hi + v4_hi + floor(v3_lo / 2^32) + W[k] + v3_lo = 0|((v3_lo + 2^31) % 2^32 - 2^31) + vE_lo, vE_hi = vE_hi ~ v3_hi, vE_lo ~ v3_lo + v9_lo = v9_lo % 2^32 + vE_lo % 2^32 + v9_hi = v9_hi + vE_hi + floor(v9_lo / 2^32) + v9_lo = 0|((v9_lo + 2^31) % 2^32 - 2^31) + v4_lo, v4_hi = v4_lo ~ v9_lo, v4_hi ~ v9_hi + v4_lo, v4_hi = v4_lo >> 24 | v4_hi << 8, v4_hi >> 24 | v4_lo << 8 + k = row[16] * 2 + v3_lo = v3_lo % 2^32 + v4_lo % 2^32 + W[k-1] % 2^32 + v3_hi = v3_hi + v4_hi + floor(v3_lo / 2^32) + W[k] + v3_lo = 0|((v3_lo + 2^31) % 2^32 - 2^31) + vE_lo, vE_hi = vE_lo ~ v3_lo, vE_hi ~ v3_hi + vE_lo, vE_hi = vE_lo >> 16 | vE_hi << 16, vE_hi >> 16 | vE_lo << 16 + v9_lo = v9_lo % 2^32 + vE_lo % 2^32 + v9_hi = v9_hi + vE_hi + floor(v9_lo / 2^32) + v9_lo = 0|((v9_lo + 2^31) % 2^32 - 2^31) + v4_lo, v4_hi = v4_lo ~ v9_lo, v4_hi ~ v9_hi + v4_lo, v4_hi = v4_lo << 1 | v4_hi >> 31, v4_hi << 1 | v4_lo >> 31 + end + h1_lo = h1_lo ~ v0_lo ~ v8_lo + h2_lo = h2_lo ~ v1_lo ~ v9_lo + h3_lo = h3_lo ~ v2_lo ~ vA_lo + h4_lo = h4_lo ~ v3_lo ~ vB_lo + h5_lo = h5_lo ~ v4_lo ~ vC_lo + h6_lo = h6_lo ~ v5_lo ~ vD_lo + h7_lo = h7_lo ~ v6_lo ~ vE_lo + h8_lo = h8_lo ~ v7_lo ~ vF_lo + h1_hi = h1_hi ~ v0_hi ~ v8_hi + h2_hi = h2_hi ~ v1_hi ~ v9_hi + h3_hi = h3_hi ~ v2_hi ~ vA_hi + h4_hi = h4_hi ~ v3_hi ~ vB_hi + h5_hi = h5_hi ~ v4_hi ~ vC_hi + h6_hi = h6_hi ~ v5_hi ~ vD_hi + h7_hi = h7_hi ~ v6_hi ~ vE_hi + h8_hi = h8_hi ~ v7_hi ~ vF_hi + end + H_lo[1], H_lo[2], H_lo[3], H_lo[4], H_lo[5], H_lo[6], H_lo[7], H_lo[8] = h1_lo, h2_lo, h3_lo, h4_lo, h5_lo, h6_lo, h7_lo, h8_lo + H_hi[1], H_hi[2], H_hi[3], H_hi[4], H_hi[5], H_hi[6], H_hi[7], H_hi[8] = h1_hi, h2_hi, h3_hi, h4_hi, h5_hi, h6_hi, h7_hi, h8_hi + return bytes_compressed + end + + local function blake3_feed_64(str, offs, size, flags, chunk_index, H_in, H_out, wide_output, block_length) + -- offs >= 0, size >= 0, size is multiple of 64 + block_length = block_length or 64 + local W = common_W + local h1, h2, h3, h4, h5, h6, h7, h8 = H_in[1], H_in[2], H_in[3], H_in[4], H_in[5], H_in[6], H_in[7], H_in[8] + H_out = H_out or H_in + for pos = offs + 1, offs + size, 64 do + if str then + W[1], W[2], W[3], W[4], W[5], W[6], W[7], W[8], W[9], W[10], W[11], W[12], W[13], W[14], W[15], W[16] = + string_unpack("> 16 | vC << 16 + v8 = v8 + vC + v4 = v4 ~ v8 + v4 = v4 >> 12 | v4 << 20 + v0 = v0 + v4 + W[perm_blake3[j + 14]] + vC = vC ~ v0 + vC = vC >> 8 | vC << 24 + v8 = v8 + vC + v4 = v4 ~ v8 + v4 = v4 >> 7 | v4 << 25 + v1 = v1 + v5 + W[perm_blake3[j + 1]] + vD = vD ~ v1 + vD = vD >> 16 | vD << 16 + v9 = v9 + vD + v5 = v5 ~ v9 + v5 = v5 >> 12 | v5 << 20 + v1 = v1 + v5 + W[perm_blake3[j + 2]] + vD = vD ~ v1 + vD = vD >> 8 | vD << 24 + v9 = v9 + vD + v5 = v5 ~ v9 + v5 = v5 >> 7 | v5 << 25 + v2 = v2 + v6 + W[perm_blake3[j + 16]] + vE = vE ~ v2 + vE = vE >> 16 | vE << 16 + vA = vA + vE + v6 = v6 ~ vA + v6 = v6 >> 12 | v6 << 20 + v2 = v2 + v6 + W[perm_blake3[j + 7]] + vE = vE ~ v2 + vE = vE >> 8 | vE << 24 + vA = vA + vE + v6 = v6 ~ vA + v6 = v6 >> 7 | v6 << 25 + v3 = v3 + v7 + W[perm_blake3[j + 15]] + vF = vF ~ v3 + vF = vF >> 16 | vF << 16 + vB = vB + vF + v7 = v7 ~ vB + v7 = v7 >> 12 | v7 << 20 + v3 = v3 + v7 + W[perm_blake3[j + 17]] + vF = vF ~ v3 + vF = vF >> 8 | vF << 24 + vB = vB + vF + v7 = v7 ~ vB + v7 = v7 >> 7 | v7 << 25 + v0 = v0 + v5 + W[perm_blake3[j + 21]] + vF = vF ~ v0 + vF = vF >> 16 | vF << 16 + vA = vA + vF + v5 = v5 ~ vA + v5 = v5 >> 12 | v5 << 20 + v0 = v0 + v5 + W[perm_blake3[j + 5]] + vF = vF ~ v0 + vF = vF >> 8 | vF << 24 + vA = vA + vF + v5 = v5 ~ vA + v5 = v5 >> 7 | v5 << 25 + v1 = v1 + v6 + W[perm_blake3[j + 3]] + vC = vC ~ v1 + vC = vC >> 16 | vC << 16 + vB = vB + vC + v6 = v6 ~ vB + v6 = v6 >> 12 | v6 << 20 + v1 = v1 + v6 + W[perm_blake3[j + 6]] + vC = vC ~ v1 + vC = vC >> 8 | vC << 24 + vB = vB + vC + v6 = v6 ~ vB + v6 = v6 >> 7 | v6 << 25 + v2 = v2 + v7 + W[perm_blake3[j + 4]] + vD = vD ~ v2 + vD = vD >> 16 | vD << 16 + v8 = v8 + vD + v7 = v7 ~ v8 + v7 = v7 >> 12 | v7 << 20 + v2 = v2 + v7 + W[perm_blake3[j + 18]] + vD = vD ~ v2 + vD = vD >> 8 | vD << 24 + v8 = v8 + vD + v7 = v7 ~ v8 + v7 = v7 >> 7 | v7 << 25 + v3 = v3 + v4 + W[perm_blake3[j + 19]] + vE = vE ~ v3 + vE = vE >> 16 | vE << 16 + v9 = v9 + vE + v4 = v4 ~ v9 + v4 = v4 >> 12 | v4 << 20 + v3 = v3 + v4 + W[perm_blake3[j + 20]] + vE = vE ~ v3 + vE = vE >> 8 | vE << 24 + v9 = v9 + vE + v4 = v4 ~ v9 + v4 = v4 >> 7 | v4 << 25 + end + if wide_output then + H_out[ 9] = h1 ~ v8 + H_out[10] = h2 ~ v9 + H_out[11] = h3 ~ vA + H_out[12] = h4 ~ vB + H_out[13] = h5 ~ vC + H_out[14] = h6 ~ vD + H_out[15] = h7 ~ vE + H_out[16] = h8 ~ vF + end + h1 = v0 ~ v8 + h2 = v1 ~ v9 + h3 = v2 ~ vA + h4 = v3 ~ vB + h5 = v4 ~ vC + h6 = v5 ~ vD + h7 = v6 ~ vE + h8 = v7 ~ vF + end + H_out[1], H_out[2], H_out[3], H_out[4], H_out[5], H_out[6], H_out[7], H_out[8] = h1, h2, h3, h4, h5, h6, h7, h8 + end + + return XORA5, XOR_BYTE, sha256_feed_64, sha512_feed_128, md5_feed_64, sha1_feed_64, keccak_feed, blake2s_feed_64, blake2b_feed_128, blake3_feed_64 + ]=](md5_next_shift, md5_K, sha2_K_lo, sha2_K_hi, build_keccak_format, sha3_RC_lo, sha3_RC_hi, sigma, common_W, sha2_H_lo, sha2_H_hi, perm_blake3) + +end + +XOR = XOR or XORA5 + +if branch == "LIB32" or branch == "EMUL" then + + + -- implementation for Lua 5.1/5.2 (with or without bitwise library available) + + function sha256_feed_64(H, str, offs, size) + -- offs >= 0, size >= 0, size is multiple of 64 + local W, K = common_W, sha2_K_hi + local h1, h2, h3, h4, h5, h6, h7, h8 = H[1], H[2], H[3], H[4], H[5], H[6], H[7], H[8] + for pos = offs, offs + size - 1, 64 do + for j = 1, 16 do + pos = pos + 4 + local a, b, c, d = byte(str, pos - 3, pos) + W[j] = ((a * 256 + b) * 256 + c) * 256 + d + end + for j = 17, 64 do + local a, b = W[j-15], W[j-2] + local a7, a18, b17, b19 = a / 2^7, a / 2^18, b / 2^17, b / 2^19 + W[j] = (XOR(a7 % 1 * (2^32 - 1) + a7, a18 % 1 * (2^32 - 1) + a18, (a - a % 2^3) / 2^3) + W[j-16] + W[j-7] + + XOR(b17 % 1 * (2^32 - 1) + b17, b19 % 1 * (2^32 - 1) + b19, (b - b % 2^10) / 2^10)) % 2^32 + end + local a, b, c, d, e, f, g, h = h1, h2, h3, h4, h5, h6, h7, h8 + for j = 1, 64 do + e = e % 2^32 + local e6, e11, e7 = e / 2^6, e / 2^11, e * 2^7 + local e7_lo = e7 % 2^32 + local z = AND(e, f) + AND(-1-e, g) + h + K[j] + W[j] + + XOR(e6 % 1 * (2^32 - 1) + e6, e11 % 1 * (2^32 - 1) + e11, e7_lo + (e7 - e7_lo) / 2^32) + h = g + g = f + f = e + e = z + d + d = c + c = b + b = a % 2^32 + local b2, b13, b10 = b / 2^2, b / 2^13, b * 2^10 + local b10_lo = b10 % 2^32 + a = z + AND(d, c) + AND(b, XOR(d, c)) + + XOR(b2 % 1 * (2^32 - 1) + b2, b13 % 1 * (2^32 - 1) + b13, b10_lo + (b10 - b10_lo) / 2^32) + end + h1, h2, h3, h4 = (a + h1) % 2^32, (b + h2) % 2^32, (c + h3) % 2^32, (d + h4) % 2^32 + h5, h6, h7, h8 = (e + h5) % 2^32, (f + h6) % 2^32, (g + h7) % 2^32, (h + h8) % 2^32 + end + H[1], H[2], H[3], H[4], H[5], H[6], H[7], H[8] = h1, h2, h3, h4, h5, h6, h7, h8 + end + + + function sha512_feed_128(H_lo, H_hi, str, offs, size) + -- offs >= 0, size >= 0, size is multiple of 128 + -- W1_hi, W1_lo, W2_hi, W2_lo, ... Wk_hi = W[2*k-1], Wk_lo = W[2*k] + local W, K_lo, K_hi = common_W, sha2_K_lo, sha2_K_hi + local h1_lo, h2_lo, h3_lo, h4_lo, h5_lo, h6_lo, h7_lo, h8_lo = H_lo[1], H_lo[2], H_lo[3], H_lo[4], H_lo[5], H_lo[6], H_lo[7], H_lo[8] + local h1_hi, h2_hi, h3_hi, h4_hi, h5_hi, h6_hi, h7_hi, h8_hi = H_hi[1], H_hi[2], H_hi[3], H_hi[4], H_hi[5], H_hi[6], H_hi[7], H_hi[8] + for pos = offs, offs + size - 1, 128 do + for j = 1, 16*2 do + pos = pos + 4 + local a, b, c, d = byte(str, pos - 3, pos) + W[j] = ((a * 256 + b) * 256 + c) * 256 + d + end + for jj = 17*2, 80*2, 2 do + local a_hi, a_lo, b_hi, b_lo = W[jj-31], W[jj-30], W[jj-5], W[jj-4] + local b_hi_6, b_hi_19, b_hi_29, b_lo_19, b_lo_29, a_hi_1, a_hi_7, a_hi_8, a_lo_1, a_lo_8 = + b_hi % 2^6, b_hi % 2^19, b_hi % 2^29, b_lo % 2^19, b_lo % 2^29, a_hi % 2^1, a_hi % 2^7, a_hi % 2^8, a_lo % 2^1, a_lo % 2^8 + local tmp1 = XOR((a_lo - a_lo_1) / 2^1 + a_hi_1 * 2^31, (a_lo - a_lo_8) / 2^8 + a_hi_8 * 2^24, (a_lo - a_lo % 2^7) / 2^7 + a_hi_7 * 2^25) % 2^32 + + XOR((b_lo - b_lo_19) / 2^19 + b_hi_19 * 2^13, b_lo_29 * 2^3 + (b_hi - b_hi_29) / 2^29, (b_lo - b_lo % 2^6) / 2^6 + b_hi_6 * 2^26) % 2^32 + + W[jj-14] + W[jj-32] + local tmp2 = tmp1 % 2^32 + W[jj-1] = (XOR((a_hi - a_hi_1) / 2^1 + a_lo_1 * 2^31, (a_hi - a_hi_8) / 2^8 + a_lo_8 * 2^24, (a_hi - a_hi_7) / 2^7) + + XOR((b_hi - b_hi_19) / 2^19 + b_lo_19 * 2^13, b_hi_29 * 2^3 + (b_lo - b_lo_29) / 2^29, (b_hi - b_hi_6) / 2^6) + + W[jj-15] + W[jj-33] + (tmp1 - tmp2) / 2^32) % 2^32 + W[jj] = tmp2 + end + local a_lo, b_lo, c_lo, d_lo, e_lo, f_lo, g_lo, h_lo = h1_lo, h2_lo, h3_lo, h4_lo, h5_lo, h6_lo, h7_lo, h8_lo + local a_hi, b_hi, c_hi, d_hi, e_hi, f_hi, g_hi, h_hi = h1_hi, h2_hi, h3_hi, h4_hi, h5_hi, h6_hi, h7_hi, h8_hi + for j = 1, 80 do + local jj = 2*j + local e_lo_9, e_lo_14, e_lo_18, e_hi_9, e_hi_14, e_hi_18 = e_lo % 2^9, e_lo % 2^14, e_lo % 2^18, e_hi % 2^9, e_hi % 2^14, e_hi % 2^18 + local tmp1 = (AND(e_lo, f_lo) + AND(-1-e_lo, g_lo)) % 2^32 + h_lo + K_lo[j] + W[jj] + + XOR((e_lo - e_lo_14) / 2^14 + e_hi_14 * 2^18, (e_lo - e_lo_18) / 2^18 + e_hi_18 * 2^14, e_lo_9 * 2^23 + (e_hi - e_hi_9) / 2^9) % 2^32 + local z_lo = tmp1 % 2^32 + local z_hi = AND(e_hi, f_hi) + AND(-1-e_hi, g_hi) + h_hi + K_hi[j] + W[jj-1] + (tmp1 - z_lo) / 2^32 + + XOR((e_hi - e_hi_14) / 2^14 + e_lo_14 * 2^18, (e_hi - e_hi_18) / 2^18 + e_lo_18 * 2^14, e_hi_9 * 2^23 + (e_lo - e_lo_9) / 2^9) + h_lo = g_lo; h_hi = g_hi + g_lo = f_lo; g_hi = f_hi + f_lo = e_lo; f_hi = e_hi + tmp1 = z_lo + d_lo + e_lo = tmp1 % 2^32 + e_hi = (z_hi + d_hi + (tmp1 - e_lo) / 2^32) % 2^32 + d_lo = c_lo; d_hi = c_hi + c_lo = b_lo; c_hi = b_hi + b_lo = a_lo; b_hi = a_hi + local b_lo_2, b_lo_7, b_lo_28, b_hi_2, b_hi_7, b_hi_28 = b_lo % 2^2, b_lo % 2^7, b_lo % 2^28, b_hi % 2^2, b_hi % 2^7, b_hi % 2^28 + tmp1 = z_lo + (AND(d_lo, c_lo) + AND(b_lo, XOR(d_lo, c_lo))) % 2^32 + + XOR((b_lo - b_lo_28) / 2^28 + b_hi_28 * 2^4, b_lo_2 * 2^30 + (b_hi - b_hi_2) / 2^2, b_lo_7 * 2^25 + (b_hi - b_hi_7) / 2^7) % 2^32 + a_lo = tmp1 % 2^32 + a_hi = (z_hi + AND(d_hi, c_hi) + AND(b_hi, XOR(d_hi, c_hi)) + (tmp1 - a_lo) / 2^32 + + XOR((b_hi - b_hi_28) / 2^28 + b_lo_28 * 2^4, b_hi_2 * 2^30 + (b_lo - b_lo_2) / 2^2, b_hi_7 * 2^25 + (b_lo - b_lo_7) / 2^7)) % 2^32 + end + a_lo = h1_lo + a_lo + h1_lo = a_lo % 2^32 + h1_hi = (h1_hi + a_hi + (a_lo - h1_lo) / 2^32) % 2^32 + a_lo = h2_lo + b_lo + h2_lo = a_lo % 2^32 + h2_hi = (h2_hi + b_hi + (a_lo - h2_lo) / 2^32) % 2^32 + a_lo = h3_lo + c_lo + h3_lo = a_lo % 2^32 + h3_hi = (h3_hi + c_hi + (a_lo - h3_lo) / 2^32) % 2^32 + a_lo = h4_lo + d_lo + h4_lo = a_lo % 2^32 + h4_hi = (h4_hi + d_hi + (a_lo - h4_lo) / 2^32) % 2^32 + a_lo = h5_lo + e_lo + h5_lo = a_lo % 2^32 + h5_hi = (h5_hi + e_hi + (a_lo - h5_lo) / 2^32) % 2^32 + a_lo = h6_lo + f_lo + h6_lo = a_lo % 2^32 + h6_hi = (h6_hi + f_hi + (a_lo - h6_lo) / 2^32) % 2^32 + a_lo = h7_lo + g_lo + h7_lo = a_lo % 2^32 + h7_hi = (h7_hi + g_hi + (a_lo - h7_lo) / 2^32) % 2^32 + a_lo = h8_lo + h_lo + h8_lo = a_lo % 2^32 + h8_hi = (h8_hi + h_hi + (a_lo - h8_lo) / 2^32) % 2^32 + end + H_lo[1], H_lo[2], H_lo[3], H_lo[4], H_lo[5], H_lo[6], H_lo[7], H_lo[8] = h1_lo, h2_lo, h3_lo, h4_lo, h5_lo, h6_lo, h7_lo, h8_lo + H_hi[1], H_hi[2], H_hi[3], H_hi[4], H_hi[5], H_hi[6], H_hi[7], H_hi[8] = h1_hi, h2_hi, h3_hi, h4_hi, h5_hi, h6_hi, h7_hi, h8_hi + end + + + if branch == "LIB32" then + + function md5_feed_64(H, str, offs, size) + -- offs >= 0, size >= 0, size is multiple of 64 + local W, K, md5_next_shift = common_W, md5_K, md5_next_shift + local h1, h2, h3, h4 = H[1], H[2], H[3], H[4] + for pos = offs, offs + size - 1, 64 do + for j = 1, 16 do + pos = pos + 4 + local a, b, c, d = byte(str, pos - 3, pos) + W[j] = ((d * 256 + c) * 256 + b) * 256 + a + end + local a, b, c, d = h1, h2, h3, h4 + local s = 25 + for j = 1, 16 do + local F = ROR(AND(b, c) + AND(-1-b, d) + a + K[j] + W[j], s) + b + s = md5_next_shift[s] + a = d + d = c + c = b + b = F + end + s = 27 + for j = 17, 32 do + local F = ROR(AND(d, b) + AND(-1-d, c) + a + K[j] + W[(5*j-4) % 16 + 1], s) + b + s = md5_next_shift[s] + a = d + d = c + c = b + b = F + end + s = 28 + for j = 33, 48 do + local F = ROR(XOR(XOR(b, c), d) + a + K[j] + W[(3*j+2) % 16 + 1], s) + b + s = md5_next_shift[s] + a = d + d = c + c = b + b = F + end + s = 26 + for j = 49, 64 do + local F = ROR(XOR(c, OR(b, -1-d)) + a + K[j] + W[(j*7-7) % 16 + 1], s) + b + s = md5_next_shift[s] + a = d + d = c + c = b + b = F + end + h1 = (a + h1) % 2^32 + h2 = (b + h2) % 2^32 + h3 = (c + h3) % 2^32 + h4 = (d + h4) % 2^32 + end + H[1], H[2], H[3], H[4] = h1, h2, h3, h4 + end + + elseif branch == "EMUL" then + + function md5_feed_64(H, str, offs, size) + -- offs >= 0, size >= 0, size is multiple of 64 + local W, K, md5_next_shift = common_W, md5_K, md5_next_shift + local h1, h2, h3, h4 = H[1], H[2], H[3], H[4] + for pos = offs, offs + size - 1, 64 do + for j = 1, 16 do + pos = pos + 4 + local a, b, c, d = byte(str, pos - 3, pos) + W[j] = ((d * 256 + c) * 256 + b) * 256 + a + end + local a, b, c, d = h1, h2, h3, h4 + local s = 25 + for j = 1, 16 do + local z = (AND(b, c) + AND(-1-b, d) + a + K[j] + W[j]) % 2^32 / 2^s + local y = z % 1 + s = md5_next_shift[s] + a = d + d = c + c = b + b = y * 2^32 + (z - y) + b + end + s = 27 + for j = 17, 32 do + local z = (AND(d, b) + AND(-1-d, c) + a + K[j] + W[(5*j-4) % 16 + 1]) % 2^32 / 2^s + local y = z % 1 + s = md5_next_shift[s] + a = d + d = c + c = b + b = y * 2^32 + (z - y) + b + end + s = 28 + for j = 33, 48 do + local z = (XOR(XOR(b, c), d) + a + K[j] + W[(3*j+2) % 16 + 1]) % 2^32 / 2^s + local y = z % 1 + s = md5_next_shift[s] + a = d + d = c + c = b + b = y * 2^32 + (z - y) + b + end + s = 26 + for j = 49, 64 do + local z = (XOR(c, OR(b, -1-d)) + a + K[j] + W[(j*7-7) % 16 + 1]) % 2^32 / 2^s + local y = z % 1 + s = md5_next_shift[s] + a = d + d = c + c = b + b = y * 2^32 + (z - y) + b + end + h1 = (a + h1) % 2^32 + h2 = (b + h2) % 2^32 + h3 = (c + h3) % 2^32 + h4 = (d + h4) % 2^32 + end + H[1], H[2], H[3], H[4] = h1, h2, h3, h4 + end + + end + + + function sha1_feed_64(H, str, offs, size) + -- offs >= 0, size >= 0, size is multiple of 64 + local W = common_W + local h1, h2, h3, h4, h5 = H[1], H[2], H[3], H[4], H[5] + for pos = offs, offs + size - 1, 64 do + for j = 1, 16 do + pos = pos + 4 + local a, b, c, d = byte(str, pos - 3, pos) + W[j] = ((a * 256 + b) * 256 + c) * 256 + d + end + for j = 17, 80 do + local a = XOR(W[j-3], W[j-8], W[j-14], W[j-16]) % 2^32 * 2 + local b = a % 2^32 + W[j] = b + (a - b) / 2^32 + end + local a, b, c, d, e = h1, h2, h3, h4, h5 + for j = 1, 20 do + local a5 = a * 2^5 + local z = a5 % 2^32 + z = z + (a5 - z) / 2^32 + AND(b, c) + AND(-1-b, d) + 0x5A827999 + W[j] + e -- constant = floor(2^30 * sqrt(2)) + e = d + d = c + c = b / 2^2 + c = c % 1 * (2^32 - 1) + c + b = a + a = z % 2^32 + end + for j = 21, 40 do + local a5 = a * 2^5 + local z = a5 % 2^32 + z = z + (a5 - z) / 2^32 + XOR(b, c, d) + 0x6ED9EBA1 + W[j] + e -- 2^30 * sqrt(3) + e = d + d = c + c = b / 2^2 + c = c % 1 * (2^32 - 1) + c + b = a + a = z % 2^32 + end + for j = 41, 60 do + local a5 = a * 2^5 + local z = a5 % 2^32 + z = z + (a5 - z) / 2^32 + AND(d, c) + AND(b, XOR(d, c)) + 0x8F1BBCDC + W[j] + e -- 2^30 * sqrt(5) + e = d + d = c + c = b / 2^2 + c = c % 1 * (2^32 - 1) + c + b = a + a = z % 2^32 + end + for j = 61, 80 do + local a5 = a * 2^5 + local z = a5 % 2^32 + z = z + (a5 - z) / 2^32 + XOR(b, c, d) + 0xCA62C1D6 + W[j] + e -- 2^30 * sqrt(10) + e = d + d = c + c = b / 2^2 + c = c % 1 * (2^32 - 1) + c + b = a + a = z % 2^32 + end + h1 = (a + h1) % 2^32 + h2 = (b + h2) % 2^32 + h3 = (c + h3) % 2^32 + h4 = (d + h4) % 2^32 + h5 = (e + h5) % 2^32 + end + H[1], H[2], H[3], H[4], H[5] = h1, h2, h3, h4, h5 + end + + + function keccak_feed(lanes_lo, lanes_hi, str, offs, size, block_size_in_bytes) + -- This is an example of a Lua function having 79 local variables :-) + -- offs >= 0, size >= 0, size is multiple of block_size_in_bytes, block_size_in_bytes is positive multiple of 8 + local RC_lo, RC_hi = sha3_RC_lo, sha3_RC_hi + local qwords_qty = block_size_in_bytes / 8 + for pos = offs, offs + size - 1, block_size_in_bytes do + for j = 1, qwords_qty do + local a, b, c, d = byte(str, pos + 1, pos + 4) + lanes_lo[j] = XOR(lanes_lo[j], ((d * 256 + c) * 256 + b) * 256 + a) + pos = pos + 8 + a, b, c, d = byte(str, pos - 3, pos) + lanes_hi[j] = XOR(lanes_hi[j], ((d * 256 + c) * 256 + b) * 256 + a) + end + local L01_lo, L01_hi, L02_lo, L02_hi, L03_lo, L03_hi, L04_lo, L04_hi, L05_lo, L05_hi, L06_lo, L06_hi, L07_lo, L07_hi, L08_lo, L08_hi, + L09_lo, L09_hi, L10_lo, L10_hi, L11_lo, L11_hi, L12_lo, L12_hi, L13_lo, L13_hi, L14_lo, L14_hi, L15_lo, L15_hi, L16_lo, L16_hi, + L17_lo, L17_hi, L18_lo, L18_hi, L19_lo, L19_hi, L20_lo, L20_hi, L21_lo, L21_hi, L22_lo, L22_hi, L23_lo, L23_hi, L24_lo, L24_hi, L25_lo, L25_hi = + lanes_lo[1], lanes_hi[1], lanes_lo[2], lanes_hi[2], lanes_lo[3], lanes_hi[3], lanes_lo[4], lanes_hi[4], lanes_lo[5], lanes_hi[5], + lanes_lo[6], lanes_hi[6], lanes_lo[7], lanes_hi[7], lanes_lo[8], lanes_hi[8], lanes_lo[9], lanes_hi[9], lanes_lo[10], lanes_hi[10], + lanes_lo[11], lanes_hi[11], lanes_lo[12], lanes_hi[12], lanes_lo[13], lanes_hi[13], lanes_lo[14], lanes_hi[14], lanes_lo[15], lanes_hi[15], + lanes_lo[16], lanes_hi[16], lanes_lo[17], lanes_hi[17], lanes_lo[18], lanes_hi[18], lanes_lo[19], lanes_hi[19], lanes_lo[20], lanes_hi[20], + lanes_lo[21], lanes_hi[21], lanes_lo[22], lanes_hi[22], lanes_lo[23], lanes_hi[23], lanes_lo[24], lanes_hi[24], lanes_lo[25], lanes_hi[25] + for round_idx = 1, 24 do + local C1_lo = XOR(L01_lo, L06_lo, L11_lo, L16_lo, L21_lo) + local C1_hi = XOR(L01_hi, L06_hi, L11_hi, L16_hi, L21_hi) + local C2_lo = XOR(L02_lo, L07_lo, L12_lo, L17_lo, L22_lo) + local C2_hi = XOR(L02_hi, L07_hi, L12_hi, L17_hi, L22_hi) + local C3_lo = XOR(L03_lo, L08_lo, L13_lo, L18_lo, L23_lo) + local C3_hi = XOR(L03_hi, L08_hi, L13_hi, L18_hi, L23_hi) + local C4_lo = XOR(L04_lo, L09_lo, L14_lo, L19_lo, L24_lo) + local C4_hi = XOR(L04_hi, L09_hi, L14_hi, L19_hi, L24_hi) + local C5_lo = XOR(L05_lo, L10_lo, L15_lo, L20_lo, L25_lo) + local C5_hi = XOR(L05_hi, L10_hi, L15_hi, L20_hi, L25_hi) + local D_lo = XOR(C1_lo, C3_lo * 2 + (C3_hi % 2^32 - C3_hi % 2^31) / 2^31) + local D_hi = XOR(C1_hi, C3_hi * 2 + (C3_lo % 2^32 - C3_lo % 2^31) / 2^31) + local T0_lo = XOR(D_lo, L02_lo) + local T0_hi = XOR(D_hi, L02_hi) + local T1_lo = XOR(D_lo, L07_lo) + local T1_hi = XOR(D_hi, L07_hi) + local T2_lo = XOR(D_lo, L12_lo) + local T2_hi = XOR(D_hi, L12_hi) + local T3_lo = XOR(D_lo, L17_lo) + local T3_hi = XOR(D_hi, L17_hi) + local T4_lo = XOR(D_lo, L22_lo) + local T4_hi = XOR(D_hi, L22_hi) + L02_lo = (T1_lo % 2^32 - T1_lo % 2^20) / 2^20 + T1_hi * 2^12 + L02_hi = (T1_hi % 2^32 - T1_hi % 2^20) / 2^20 + T1_lo * 2^12 + L07_lo = (T3_lo % 2^32 - T3_lo % 2^19) / 2^19 + T3_hi * 2^13 + L07_hi = (T3_hi % 2^32 - T3_hi % 2^19) / 2^19 + T3_lo * 2^13 + L12_lo = T0_lo * 2 + (T0_hi % 2^32 - T0_hi % 2^31) / 2^31 + L12_hi = T0_hi * 2 + (T0_lo % 2^32 - T0_lo % 2^31) / 2^31 + L17_lo = T2_lo * 2^10 + (T2_hi % 2^32 - T2_hi % 2^22) / 2^22 + L17_hi = T2_hi * 2^10 + (T2_lo % 2^32 - T2_lo % 2^22) / 2^22 + L22_lo = T4_lo * 2^2 + (T4_hi % 2^32 - T4_hi % 2^30) / 2^30 + L22_hi = T4_hi * 2^2 + (T4_lo % 2^32 - T4_lo % 2^30) / 2^30 + D_lo = XOR(C2_lo, C4_lo * 2 + (C4_hi % 2^32 - C4_hi % 2^31) / 2^31) + D_hi = XOR(C2_hi, C4_hi * 2 + (C4_lo % 2^32 - C4_lo % 2^31) / 2^31) + T0_lo = XOR(D_lo, L03_lo) + T0_hi = XOR(D_hi, L03_hi) + T1_lo = XOR(D_lo, L08_lo) + T1_hi = XOR(D_hi, L08_hi) + T2_lo = XOR(D_lo, L13_lo) + T2_hi = XOR(D_hi, L13_hi) + T3_lo = XOR(D_lo, L18_lo) + T3_hi = XOR(D_hi, L18_hi) + T4_lo = XOR(D_lo, L23_lo) + T4_hi = XOR(D_hi, L23_hi) + L03_lo = (T2_lo % 2^32 - T2_lo % 2^21) / 2^21 + T2_hi * 2^11 + L03_hi = (T2_hi % 2^32 - T2_hi % 2^21) / 2^21 + T2_lo * 2^11 + L08_lo = (T4_lo % 2^32 - T4_lo % 2^3) / 2^3 + T4_hi * 2^29 % 2^32 + L08_hi = (T4_hi % 2^32 - T4_hi % 2^3) / 2^3 + T4_lo * 2^29 % 2^32 + L13_lo = T1_lo * 2^6 + (T1_hi % 2^32 - T1_hi % 2^26) / 2^26 + L13_hi = T1_hi * 2^6 + (T1_lo % 2^32 - T1_lo % 2^26) / 2^26 + L18_lo = T3_lo * 2^15 + (T3_hi % 2^32 - T3_hi % 2^17) / 2^17 + L18_hi = T3_hi * 2^15 + (T3_lo % 2^32 - T3_lo % 2^17) / 2^17 + L23_lo = (T0_lo % 2^32 - T0_lo % 2^2) / 2^2 + T0_hi * 2^30 % 2^32 + L23_hi = (T0_hi % 2^32 - T0_hi % 2^2) / 2^2 + T0_lo * 2^30 % 2^32 + D_lo = XOR(C3_lo, C5_lo * 2 + (C5_hi % 2^32 - C5_hi % 2^31) / 2^31) + D_hi = XOR(C3_hi, C5_hi * 2 + (C5_lo % 2^32 - C5_lo % 2^31) / 2^31) + T0_lo = XOR(D_lo, L04_lo) + T0_hi = XOR(D_hi, L04_hi) + T1_lo = XOR(D_lo, L09_lo) + T1_hi = XOR(D_hi, L09_hi) + T2_lo = XOR(D_lo, L14_lo) + T2_hi = XOR(D_hi, L14_hi) + T3_lo = XOR(D_lo, L19_lo) + T3_hi = XOR(D_hi, L19_hi) + T4_lo = XOR(D_lo, L24_lo) + T4_hi = XOR(D_hi, L24_hi) + L04_lo = T3_lo * 2^21 % 2^32 + (T3_hi % 2^32 - T3_hi % 2^11) / 2^11 + L04_hi = T3_hi * 2^21 % 2^32 + (T3_lo % 2^32 - T3_lo % 2^11) / 2^11 + L09_lo = T0_lo * 2^28 % 2^32 + (T0_hi % 2^32 - T0_hi % 2^4) / 2^4 + L09_hi = T0_hi * 2^28 % 2^32 + (T0_lo % 2^32 - T0_lo % 2^4) / 2^4 + L14_lo = T2_lo * 2^25 % 2^32 + (T2_hi % 2^32 - T2_hi % 2^7) / 2^7 + L14_hi = T2_hi * 2^25 % 2^32 + (T2_lo % 2^32 - T2_lo % 2^7) / 2^7 + L19_lo = (T4_lo % 2^32 - T4_lo % 2^8) / 2^8 + T4_hi * 2^24 % 2^32 + L19_hi = (T4_hi % 2^32 - T4_hi % 2^8) / 2^8 + T4_lo * 2^24 % 2^32 + L24_lo = (T1_lo % 2^32 - T1_lo % 2^9) / 2^9 + T1_hi * 2^23 % 2^32 + L24_hi = (T1_hi % 2^32 - T1_hi % 2^9) / 2^9 + T1_lo * 2^23 % 2^32 + D_lo = XOR(C4_lo, C1_lo * 2 + (C1_hi % 2^32 - C1_hi % 2^31) / 2^31) + D_hi = XOR(C4_hi, C1_hi * 2 + (C1_lo % 2^32 - C1_lo % 2^31) / 2^31) + T0_lo = XOR(D_lo, L05_lo) + T0_hi = XOR(D_hi, L05_hi) + T1_lo = XOR(D_lo, L10_lo) + T1_hi = XOR(D_hi, L10_hi) + T2_lo = XOR(D_lo, L15_lo) + T2_hi = XOR(D_hi, L15_hi) + T3_lo = XOR(D_lo, L20_lo) + T3_hi = XOR(D_hi, L20_hi) + T4_lo = XOR(D_lo, L25_lo) + T4_hi = XOR(D_hi, L25_hi) + L05_lo = T4_lo * 2^14 + (T4_hi % 2^32 - T4_hi % 2^18) / 2^18 + L05_hi = T4_hi * 2^14 + (T4_lo % 2^32 - T4_lo % 2^18) / 2^18 + L10_lo = T1_lo * 2^20 % 2^32 + (T1_hi % 2^32 - T1_hi % 2^12) / 2^12 + L10_hi = T1_hi * 2^20 % 2^32 + (T1_lo % 2^32 - T1_lo % 2^12) / 2^12 + L15_lo = T3_lo * 2^8 + (T3_hi % 2^32 - T3_hi % 2^24) / 2^24 + L15_hi = T3_hi * 2^8 + (T3_lo % 2^32 - T3_lo % 2^24) / 2^24 + L20_lo = T0_lo * 2^27 % 2^32 + (T0_hi % 2^32 - T0_hi % 2^5) / 2^5 + L20_hi = T0_hi * 2^27 % 2^32 + (T0_lo % 2^32 - T0_lo % 2^5) / 2^5 + L25_lo = (T2_lo % 2^32 - T2_lo % 2^25) / 2^25 + T2_hi * 2^7 + L25_hi = (T2_hi % 2^32 - T2_hi % 2^25) / 2^25 + T2_lo * 2^7 + D_lo = XOR(C5_lo, C2_lo * 2 + (C2_hi % 2^32 - C2_hi % 2^31) / 2^31) + D_hi = XOR(C5_hi, C2_hi * 2 + (C2_lo % 2^32 - C2_lo % 2^31) / 2^31) + T1_lo = XOR(D_lo, L06_lo) + T1_hi = XOR(D_hi, L06_hi) + T2_lo = XOR(D_lo, L11_lo) + T2_hi = XOR(D_hi, L11_hi) + T3_lo = XOR(D_lo, L16_lo) + T3_hi = XOR(D_hi, L16_hi) + T4_lo = XOR(D_lo, L21_lo) + T4_hi = XOR(D_hi, L21_hi) + L06_lo = T2_lo * 2^3 + (T2_hi % 2^32 - T2_hi % 2^29) / 2^29 + L06_hi = T2_hi * 2^3 + (T2_lo % 2^32 - T2_lo % 2^29) / 2^29 + L11_lo = T4_lo * 2^18 + (T4_hi % 2^32 - T4_hi % 2^14) / 2^14 + L11_hi = T4_hi * 2^18 + (T4_lo % 2^32 - T4_lo % 2^14) / 2^14 + L16_lo = (T1_lo % 2^32 - T1_lo % 2^28) / 2^28 + T1_hi * 2^4 + L16_hi = (T1_hi % 2^32 - T1_hi % 2^28) / 2^28 + T1_lo * 2^4 + L21_lo = (T3_lo % 2^32 - T3_lo % 2^23) / 2^23 + T3_hi * 2^9 + L21_hi = (T3_hi % 2^32 - T3_hi % 2^23) / 2^23 + T3_lo * 2^9 + L01_lo = XOR(D_lo, L01_lo) + L01_hi = XOR(D_hi, L01_hi) + L01_lo, L02_lo, L03_lo, L04_lo, L05_lo = XOR(L01_lo, AND(-1-L02_lo, L03_lo)), XOR(L02_lo, AND(-1-L03_lo, L04_lo)), XOR(L03_lo, AND(-1-L04_lo, L05_lo)), XOR(L04_lo, AND(-1-L05_lo, L01_lo)), XOR(L05_lo, AND(-1-L01_lo, L02_lo)) + L01_hi, L02_hi, L03_hi, L04_hi, L05_hi = XOR(L01_hi, AND(-1-L02_hi, L03_hi)), XOR(L02_hi, AND(-1-L03_hi, L04_hi)), XOR(L03_hi, AND(-1-L04_hi, L05_hi)), XOR(L04_hi, AND(-1-L05_hi, L01_hi)), XOR(L05_hi, AND(-1-L01_hi, L02_hi)) + L06_lo, L07_lo, L08_lo, L09_lo, L10_lo = XOR(L09_lo, AND(-1-L10_lo, L06_lo)), XOR(L10_lo, AND(-1-L06_lo, L07_lo)), XOR(L06_lo, AND(-1-L07_lo, L08_lo)), XOR(L07_lo, AND(-1-L08_lo, L09_lo)), XOR(L08_lo, AND(-1-L09_lo, L10_lo)) + L06_hi, L07_hi, L08_hi, L09_hi, L10_hi = XOR(L09_hi, AND(-1-L10_hi, L06_hi)), XOR(L10_hi, AND(-1-L06_hi, L07_hi)), XOR(L06_hi, AND(-1-L07_hi, L08_hi)), XOR(L07_hi, AND(-1-L08_hi, L09_hi)), XOR(L08_hi, AND(-1-L09_hi, L10_hi)) + L11_lo, L12_lo, L13_lo, L14_lo, L15_lo = XOR(L12_lo, AND(-1-L13_lo, L14_lo)), XOR(L13_lo, AND(-1-L14_lo, L15_lo)), XOR(L14_lo, AND(-1-L15_lo, L11_lo)), XOR(L15_lo, AND(-1-L11_lo, L12_lo)), XOR(L11_lo, AND(-1-L12_lo, L13_lo)) + L11_hi, L12_hi, L13_hi, L14_hi, L15_hi = XOR(L12_hi, AND(-1-L13_hi, L14_hi)), XOR(L13_hi, AND(-1-L14_hi, L15_hi)), XOR(L14_hi, AND(-1-L15_hi, L11_hi)), XOR(L15_hi, AND(-1-L11_hi, L12_hi)), XOR(L11_hi, AND(-1-L12_hi, L13_hi)) + L16_lo, L17_lo, L18_lo, L19_lo, L20_lo = XOR(L20_lo, AND(-1-L16_lo, L17_lo)), XOR(L16_lo, AND(-1-L17_lo, L18_lo)), XOR(L17_lo, AND(-1-L18_lo, L19_lo)), XOR(L18_lo, AND(-1-L19_lo, L20_lo)), XOR(L19_lo, AND(-1-L20_lo, L16_lo)) + L16_hi, L17_hi, L18_hi, L19_hi, L20_hi = XOR(L20_hi, AND(-1-L16_hi, L17_hi)), XOR(L16_hi, AND(-1-L17_hi, L18_hi)), XOR(L17_hi, AND(-1-L18_hi, L19_hi)), XOR(L18_hi, AND(-1-L19_hi, L20_hi)), XOR(L19_hi, AND(-1-L20_hi, L16_hi)) + L21_lo, L22_lo, L23_lo, L24_lo, L25_lo = XOR(L23_lo, AND(-1-L24_lo, L25_lo)), XOR(L24_lo, AND(-1-L25_lo, L21_lo)), XOR(L25_lo, AND(-1-L21_lo, L22_lo)), XOR(L21_lo, AND(-1-L22_lo, L23_lo)), XOR(L22_lo, AND(-1-L23_lo, L24_lo)) + L21_hi, L22_hi, L23_hi, L24_hi, L25_hi = XOR(L23_hi, AND(-1-L24_hi, L25_hi)), XOR(L24_hi, AND(-1-L25_hi, L21_hi)), XOR(L25_hi, AND(-1-L21_hi, L22_hi)), XOR(L21_hi, AND(-1-L22_hi, L23_hi)), XOR(L22_hi, AND(-1-L23_hi, L24_hi)) + L01_lo = XOR(L01_lo, RC_lo[round_idx]) + L01_hi = L01_hi + RC_hi[round_idx] -- RC_hi[] is either 0 or 0x80000000, so we could use fast addition instead of slow XOR + end + lanes_lo[1] = L01_lo; lanes_hi[1] = L01_hi + lanes_lo[2] = L02_lo; lanes_hi[2] = L02_hi + lanes_lo[3] = L03_lo; lanes_hi[3] = L03_hi + lanes_lo[4] = L04_lo; lanes_hi[4] = L04_hi + lanes_lo[5] = L05_lo; lanes_hi[5] = L05_hi + lanes_lo[6] = L06_lo; lanes_hi[6] = L06_hi + lanes_lo[7] = L07_lo; lanes_hi[7] = L07_hi + lanes_lo[8] = L08_lo; lanes_hi[8] = L08_hi + lanes_lo[9] = L09_lo; lanes_hi[9] = L09_hi + lanes_lo[10] = L10_lo; lanes_hi[10] = L10_hi + lanes_lo[11] = L11_lo; lanes_hi[11] = L11_hi + lanes_lo[12] = L12_lo; lanes_hi[12] = L12_hi + lanes_lo[13] = L13_lo; lanes_hi[13] = L13_hi + lanes_lo[14] = L14_lo; lanes_hi[14] = L14_hi + lanes_lo[15] = L15_lo; lanes_hi[15] = L15_hi + lanes_lo[16] = L16_lo; lanes_hi[16] = L16_hi + lanes_lo[17] = L17_lo; lanes_hi[17] = L17_hi + lanes_lo[18] = L18_lo; lanes_hi[18] = L18_hi + lanes_lo[19] = L19_lo; lanes_hi[19] = L19_hi + lanes_lo[20] = L20_lo; lanes_hi[20] = L20_hi + lanes_lo[21] = L21_lo; lanes_hi[21] = L21_hi + lanes_lo[22] = L22_lo; lanes_hi[22] = L22_hi + lanes_lo[23] = L23_lo; lanes_hi[23] = L23_hi + lanes_lo[24] = L24_lo; lanes_hi[24] = L24_hi + lanes_lo[25] = L25_lo; lanes_hi[25] = L25_hi + end + end + + + function blake2s_feed_64(H, str, offs, size, bytes_compressed, last_block_size, is_last_node) + -- offs >= 0, size >= 0, size is multiple of 64 + local W = common_W + local h1, h2, h3, h4, h5, h6, h7, h8 = H[1], H[2], H[3], H[4], H[5], H[6], H[7], H[8] + for pos = offs, offs + size - 1, 64 do + if str then + for j = 1, 16 do + pos = pos + 4 + local a, b, c, d = byte(str, pos - 3, pos) + W[j] = ((d * 256 + c) * 256 + b) * 256 + a + end + end + local v0, v1, v2, v3, v4, v5, v6, v7 = h1, h2, h3, h4, h5, h6, h7, h8 + local v8, v9, vA, vB, vC, vD, vE, vF = sha2_H_hi[1], sha2_H_hi[2], sha2_H_hi[3], sha2_H_hi[4], sha2_H_hi[5], sha2_H_hi[6], sha2_H_hi[7], sha2_H_hi[8] + bytes_compressed = bytes_compressed + (last_block_size or 64) + local t0 = bytes_compressed % 2^32 + local t1 = (bytes_compressed - t0) / 2^32 + vC = XOR(vC, t0) -- t0 = low_4_bytes(bytes_compressed) + vD = XOR(vD, t1) -- t1 = high_4_bytes(bytes_compressed) + if last_block_size then -- flag f0 + vE = -1 - vE + end + if is_last_node then -- flag f1 + vF = -1 - vF + end + for j = 1, 10 do + local row = sigma[j] + v0 = v0 + v4 + W[row[1]] + vC = XOR(vC, v0) % 2^32 / 2^16 + vC = vC % 1 * (2^32 - 1) + vC + v8 = v8 + vC + v4 = XOR(v4, v8) % 2^32 / 2^12 + v4 = v4 % 1 * (2^32 - 1) + v4 + v0 = v0 + v4 + W[row[2]] + vC = XOR(vC, v0) % 2^32 / 2^8 + vC = vC % 1 * (2^32 - 1) + vC + v8 = v8 + vC + v4 = XOR(v4, v8) % 2^32 / 2^7 + v4 = v4 % 1 * (2^32 - 1) + v4 + v1 = v1 + v5 + W[row[3]] + vD = XOR(vD, v1) % 2^32 / 2^16 + vD = vD % 1 * (2^32 - 1) + vD + v9 = v9 + vD + v5 = XOR(v5, v9) % 2^32 / 2^12 + v5 = v5 % 1 * (2^32 - 1) + v5 + v1 = v1 + v5 + W[row[4]] + vD = XOR(vD, v1) % 2^32 / 2^8 + vD = vD % 1 * (2^32 - 1) + vD + v9 = v9 + vD + v5 = XOR(v5, v9) % 2^32 / 2^7 + v5 = v5 % 1 * (2^32 - 1) + v5 + v2 = v2 + v6 + W[row[5]] + vE = XOR(vE, v2) % 2^32 / 2^16 + vE = vE % 1 * (2^32 - 1) + vE + vA = vA + vE + v6 = XOR(v6, vA) % 2^32 / 2^12 + v6 = v6 % 1 * (2^32 - 1) + v6 + v2 = v2 + v6 + W[row[6]] + vE = XOR(vE, v2) % 2^32 / 2^8 + vE = vE % 1 * (2^32 - 1) + vE + vA = vA + vE + v6 = XOR(v6, vA) % 2^32 / 2^7 + v6 = v6 % 1 * (2^32 - 1) + v6 + v3 = v3 + v7 + W[row[7]] + vF = XOR(vF, v3) % 2^32 / 2^16 + vF = vF % 1 * (2^32 - 1) + vF + vB = vB + vF + v7 = XOR(v7, vB) % 2^32 / 2^12 + v7 = v7 % 1 * (2^32 - 1) + v7 + v3 = v3 + v7 + W[row[8]] + vF = XOR(vF, v3) % 2^32 / 2^8 + vF = vF % 1 * (2^32 - 1) + vF + vB = vB + vF + v7 = XOR(v7, vB) % 2^32 / 2^7 + v7 = v7 % 1 * (2^32 - 1) + v7 + v0 = v0 + v5 + W[row[9]] + vF = XOR(vF, v0) % 2^32 / 2^16 + vF = vF % 1 * (2^32 - 1) + vF + vA = vA + vF + v5 = XOR(v5, vA) % 2^32 / 2^12 + v5 = v5 % 1 * (2^32 - 1) + v5 + v0 = v0 + v5 + W[row[10]] + vF = XOR(vF, v0) % 2^32 / 2^8 + vF = vF % 1 * (2^32 - 1) + vF + vA = vA + vF + v5 = XOR(v5, vA) % 2^32 / 2^7 + v5 = v5 % 1 * (2^32 - 1) + v5 + v1 = v1 + v6 + W[row[11]] + vC = XOR(vC, v1) % 2^32 / 2^16 + vC = vC % 1 * (2^32 - 1) + vC + vB = vB + vC + v6 = XOR(v6, vB) % 2^32 / 2^12 + v6 = v6 % 1 * (2^32 - 1) + v6 + v1 = v1 + v6 + W[row[12]] + vC = XOR(vC, v1) % 2^32 / 2^8 + vC = vC % 1 * (2^32 - 1) + vC + vB = vB + vC + v6 = XOR(v6, vB) % 2^32 / 2^7 + v6 = v6 % 1 * (2^32 - 1) + v6 + v2 = v2 + v7 + W[row[13]] + vD = XOR(vD, v2) % 2^32 / 2^16 + vD = vD % 1 * (2^32 - 1) + vD + v8 = v8 + vD + v7 = XOR(v7, v8) % 2^32 / 2^12 + v7 = v7 % 1 * (2^32 - 1) + v7 + v2 = v2 + v7 + W[row[14]] + vD = XOR(vD, v2) % 2^32 / 2^8 + vD = vD % 1 * (2^32 - 1) + vD + v8 = v8 + vD + v7 = XOR(v7, v8) % 2^32 / 2^7 + v7 = v7 % 1 * (2^32 - 1) + v7 + v3 = v3 + v4 + W[row[15]] + vE = XOR(vE, v3) % 2^32 / 2^16 + vE = vE % 1 * (2^32 - 1) + vE + v9 = v9 + vE + v4 = XOR(v4, v9) % 2^32 / 2^12 + v4 = v4 % 1 * (2^32 - 1) + v4 + v3 = v3 + v4 + W[row[16]] + vE = XOR(vE, v3) % 2^32 / 2^8 + vE = vE % 1 * (2^32 - 1) + vE + v9 = v9 + vE + v4 = XOR(v4, v9) % 2^32 / 2^7 + v4 = v4 % 1 * (2^32 - 1) + v4 + end + h1 = XOR(h1, v0, v8) + h2 = XOR(h2, v1, v9) + h3 = XOR(h3, v2, vA) + h4 = XOR(h4, v3, vB) + h5 = XOR(h5, v4, vC) + h6 = XOR(h6, v5, vD) + h7 = XOR(h7, v6, vE) + h8 = XOR(h8, v7, vF) + end + H[1], H[2], H[3], H[4], H[5], H[6], H[7], H[8] = h1, h2, h3, h4, h5, h6, h7, h8 + return bytes_compressed + end + + + function blake2b_feed_128(H_lo, H_hi, str, offs, size, bytes_compressed, last_block_size, is_last_node) + -- offs >= 0, size >= 0, size is multiple of 128 + local W = common_W + local h1_lo, h2_lo, h3_lo, h4_lo, h5_lo, h6_lo, h7_lo, h8_lo = H_lo[1], H_lo[2], H_lo[3], H_lo[4], H_lo[5], H_lo[6], H_lo[7], H_lo[8] + local h1_hi, h2_hi, h3_hi, h4_hi, h5_hi, h6_hi, h7_hi, h8_hi = H_hi[1], H_hi[2], H_hi[3], H_hi[4], H_hi[5], H_hi[6], H_hi[7], H_hi[8] + for pos = offs, offs + size - 1, 128 do + if str then + for j = 1, 32 do + pos = pos + 4 + local a, b, c, d = byte(str, pos - 3, pos) + W[j] = ((d * 256 + c) * 256 + b) * 256 + a + end + end + local v0_lo, v1_lo, v2_lo, v3_lo, v4_lo, v5_lo, v6_lo, v7_lo = h1_lo, h2_lo, h3_lo, h4_lo, h5_lo, h6_lo, h7_lo, h8_lo + local v0_hi, v1_hi, v2_hi, v3_hi, v4_hi, v5_hi, v6_hi, v7_hi = h1_hi, h2_hi, h3_hi, h4_hi, h5_hi, h6_hi, h7_hi, h8_hi + local v8_lo, v9_lo, vA_lo, vB_lo, vC_lo, vD_lo, vE_lo, vF_lo = sha2_H_lo[1], sha2_H_lo[2], sha2_H_lo[3], sha2_H_lo[4], sha2_H_lo[5], sha2_H_lo[6], sha2_H_lo[7], sha2_H_lo[8] + local v8_hi, v9_hi, vA_hi, vB_hi, vC_hi, vD_hi, vE_hi, vF_hi = sha2_H_hi[1], sha2_H_hi[2], sha2_H_hi[3], sha2_H_hi[4], sha2_H_hi[5], sha2_H_hi[6], sha2_H_hi[7], sha2_H_hi[8] + bytes_compressed = bytes_compressed + (last_block_size or 128) + local t0_lo = bytes_compressed % 2^32 + local t0_hi = (bytes_compressed - t0_lo) / 2^32 + vC_lo = XOR(vC_lo, t0_lo) -- t0 = low_8_bytes(bytes_compressed) + vC_hi = XOR(vC_hi, t0_hi) + -- t1 = high_8_bytes(bytes_compressed) = 0, message length is always below 2^53 bytes + if last_block_size then -- flag f0 + vE_lo = -1 - vE_lo + vE_hi = -1 - vE_hi + end + if is_last_node then -- flag f1 + vF_lo = -1 - vF_lo + vF_hi = -1 - vF_hi + end + for j = 1, 12 do + local row = sigma[j] + local k = row[1] * 2 + local z = v0_lo % 2^32 + v4_lo % 2^32 + W[k-1] + v0_lo = z % 2^32 + v0_hi = v0_hi + v4_hi + (z - v0_lo) / 2^32 + W[k] + vC_lo, vC_hi = XOR(vC_hi, v0_hi), XOR(vC_lo, v0_lo) + z = v8_lo % 2^32 + vC_lo % 2^32 + v8_lo = z % 2^32 + v8_hi = v8_hi + vC_hi + (z - v8_lo) / 2^32 + v4_lo, v4_hi = XOR(v4_lo, v8_lo), XOR(v4_hi, v8_hi) + local z_lo, z_hi = v4_lo % 2^24, v4_hi % 2^24 + v4_lo, v4_hi = (v4_lo - z_lo) / 2^24 % 2^8 + z_hi * 2^8, (v4_hi - z_hi) / 2^24 % 2^8 + z_lo * 2^8 + k = row[2] * 2 + z = v0_lo % 2^32 + v4_lo % 2^32 + W[k-1] + v0_lo = z % 2^32 + v0_hi = v0_hi + v4_hi + (z - v0_lo) / 2^32 + W[k] + vC_lo, vC_hi = XOR(vC_lo, v0_lo), XOR(vC_hi, v0_hi) + z_lo, z_hi = vC_lo % 2^16, vC_hi % 2^16 + vC_lo, vC_hi = (vC_lo - z_lo) / 2^16 % 2^16 + z_hi * 2^16, (vC_hi - z_hi) / 2^16 % 2^16 + z_lo * 2^16 + z = v8_lo % 2^32 + vC_lo % 2^32 + v8_lo = z % 2^32 + v8_hi = v8_hi + vC_hi + (z - v8_lo) / 2^32 + v4_lo, v4_hi = XOR(v4_lo, v8_lo), XOR(v4_hi, v8_hi) + z_lo, z_hi = v4_lo % 2^31, v4_hi % 2^31 + v4_lo, v4_hi = z_lo * 2^1 + (v4_hi - z_hi) / 2^31 % 2^1, z_hi * 2^1 + (v4_lo - z_lo) / 2^31 % 2^1 + k = row[3] * 2 + z = v1_lo % 2^32 + v5_lo % 2^32 + W[k-1] + v1_lo = z % 2^32 + v1_hi = v1_hi + v5_hi + (z - v1_lo) / 2^32 + W[k] + vD_lo, vD_hi = XOR(vD_hi, v1_hi), XOR(vD_lo, v1_lo) + z = v9_lo % 2^32 + vD_lo % 2^32 + v9_lo = z % 2^32 + v9_hi = v9_hi + vD_hi + (z - v9_lo) / 2^32 + v5_lo, v5_hi = XOR(v5_lo, v9_lo), XOR(v5_hi, v9_hi) + z_lo, z_hi = v5_lo % 2^24, v5_hi % 2^24 + v5_lo, v5_hi = (v5_lo - z_lo) / 2^24 % 2^8 + z_hi * 2^8, (v5_hi - z_hi) / 2^24 % 2^8 + z_lo * 2^8 + k = row[4] * 2 + z = v1_lo % 2^32 + v5_lo % 2^32 + W[k-1] + v1_lo = z % 2^32 + v1_hi = v1_hi + v5_hi + (z - v1_lo) / 2^32 + W[k] + vD_lo, vD_hi = XOR(vD_lo, v1_lo), XOR(vD_hi, v1_hi) + z_lo, z_hi = vD_lo % 2^16, vD_hi % 2^16 + vD_lo, vD_hi = (vD_lo - z_lo) / 2^16 % 2^16 + z_hi * 2^16, (vD_hi - z_hi) / 2^16 % 2^16 + z_lo * 2^16 + z = v9_lo % 2^32 + vD_lo % 2^32 + v9_lo = z % 2^32 + v9_hi = v9_hi + vD_hi + (z - v9_lo) / 2^32 + v5_lo, v5_hi = XOR(v5_lo, v9_lo), XOR(v5_hi, v9_hi) + z_lo, z_hi = v5_lo % 2^31, v5_hi % 2^31 + v5_lo, v5_hi = z_lo * 2^1 + (v5_hi - z_hi) / 2^31 % 2^1, z_hi * 2^1 + (v5_lo - z_lo) / 2^31 % 2^1 + k = row[5] * 2 + z = v2_lo % 2^32 + v6_lo % 2^32 + W[k-1] + v2_lo = z % 2^32 + v2_hi = v2_hi + v6_hi + (z - v2_lo) / 2^32 + W[k] + vE_lo, vE_hi = XOR(vE_hi, v2_hi), XOR(vE_lo, v2_lo) + z = vA_lo % 2^32 + vE_lo % 2^32 + vA_lo = z % 2^32 + vA_hi = vA_hi + vE_hi + (z - vA_lo) / 2^32 + v6_lo, v6_hi = XOR(v6_lo, vA_lo), XOR(v6_hi, vA_hi) + z_lo, z_hi = v6_lo % 2^24, v6_hi % 2^24 + v6_lo, v6_hi = (v6_lo - z_lo) / 2^24 % 2^8 + z_hi * 2^8, (v6_hi - z_hi) / 2^24 % 2^8 + z_lo * 2^8 + k = row[6] * 2 + z = v2_lo % 2^32 + v6_lo % 2^32 + W[k-1] + v2_lo = z % 2^32 + v2_hi = v2_hi + v6_hi + (z - v2_lo) / 2^32 + W[k] + vE_lo, vE_hi = XOR(vE_lo, v2_lo), XOR(vE_hi, v2_hi) + z_lo, z_hi = vE_lo % 2^16, vE_hi % 2^16 + vE_lo, vE_hi = (vE_lo - z_lo) / 2^16 % 2^16 + z_hi * 2^16, (vE_hi - z_hi) / 2^16 % 2^16 + z_lo * 2^16 + z = vA_lo % 2^32 + vE_lo % 2^32 + vA_lo = z % 2^32 + vA_hi = vA_hi + vE_hi + (z - vA_lo) / 2^32 + v6_lo, v6_hi = XOR(v6_lo, vA_lo), XOR(v6_hi, vA_hi) + z_lo, z_hi = v6_lo % 2^31, v6_hi % 2^31 + v6_lo, v6_hi = z_lo * 2^1 + (v6_hi - z_hi) / 2^31 % 2^1, z_hi * 2^1 + (v6_lo - z_lo) / 2^31 % 2^1 + k = row[7] * 2 + z = v3_lo % 2^32 + v7_lo % 2^32 + W[k-1] + v3_lo = z % 2^32 + v3_hi = v3_hi + v7_hi + (z - v3_lo) / 2^32 + W[k] + vF_lo, vF_hi = XOR(vF_hi, v3_hi), XOR(vF_lo, v3_lo) + z = vB_lo % 2^32 + vF_lo % 2^32 + vB_lo = z % 2^32 + vB_hi = vB_hi + vF_hi + (z - vB_lo) / 2^32 + v7_lo, v7_hi = XOR(v7_lo, vB_lo), XOR(v7_hi, vB_hi) + z_lo, z_hi = v7_lo % 2^24, v7_hi % 2^24 + v7_lo, v7_hi = (v7_lo - z_lo) / 2^24 % 2^8 + z_hi * 2^8, (v7_hi - z_hi) / 2^24 % 2^8 + z_lo * 2^8 + k = row[8] * 2 + z = v3_lo % 2^32 + v7_lo % 2^32 + W[k-1] + v3_lo = z % 2^32 + v3_hi = v3_hi + v7_hi + (z - v3_lo) / 2^32 + W[k] + vF_lo, vF_hi = XOR(vF_lo, v3_lo), XOR(vF_hi, v3_hi) + z_lo, z_hi = vF_lo % 2^16, vF_hi % 2^16 + vF_lo, vF_hi = (vF_lo - z_lo) / 2^16 % 2^16 + z_hi * 2^16, (vF_hi - z_hi) / 2^16 % 2^16 + z_lo * 2^16 + z = vB_lo % 2^32 + vF_lo % 2^32 + vB_lo = z % 2^32 + vB_hi = vB_hi + vF_hi + (z - vB_lo) / 2^32 + v7_lo, v7_hi = XOR(v7_lo, vB_lo), XOR(v7_hi, vB_hi) + z_lo, z_hi = v7_lo % 2^31, v7_hi % 2^31 + v7_lo, v7_hi = z_lo * 2^1 + (v7_hi - z_hi) / 2^31 % 2^1, z_hi * 2^1 + (v7_lo - z_lo) / 2^31 % 2^1 + k = row[9] * 2 + z = v0_lo % 2^32 + v5_lo % 2^32 + W[k-1] + v0_lo = z % 2^32 + v0_hi = v0_hi + v5_hi + (z - v0_lo) / 2^32 + W[k] + vF_lo, vF_hi = XOR(vF_hi, v0_hi), XOR(vF_lo, v0_lo) + z = vA_lo % 2^32 + vF_lo % 2^32 + vA_lo = z % 2^32 + vA_hi = vA_hi + vF_hi + (z - vA_lo) / 2^32 + v5_lo, v5_hi = XOR(v5_lo, vA_lo), XOR(v5_hi, vA_hi) + z_lo, z_hi = v5_lo % 2^24, v5_hi % 2^24 + v5_lo, v5_hi = (v5_lo - z_lo) / 2^24 % 2^8 + z_hi * 2^8, (v5_hi - z_hi) / 2^24 % 2^8 + z_lo * 2^8 + k = row[10] * 2 + z = v0_lo % 2^32 + v5_lo % 2^32 + W[k-1] + v0_lo = z % 2^32 + v0_hi = v0_hi + v5_hi + (z - v0_lo) / 2^32 + W[k] + vF_lo, vF_hi = XOR(vF_lo, v0_lo), XOR(vF_hi, v0_hi) + z_lo, z_hi = vF_lo % 2^16, vF_hi % 2^16 + vF_lo, vF_hi = (vF_lo - z_lo) / 2^16 % 2^16 + z_hi * 2^16, (vF_hi - z_hi) / 2^16 % 2^16 + z_lo * 2^16 + z = vA_lo % 2^32 + vF_lo % 2^32 + vA_lo = z % 2^32 + vA_hi = vA_hi + vF_hi + (z - vA_lo) / 2^32 + v5_lo, v5_hi = XOR(v5_lo, vA_lo), XOR(v5_hi, vA_hi) + z_lo, z_hi = v5_lo % 2^31, v5_hi % 2^31 + v5_lo, v5_hi = z_lo * 2^1 + (v5_hi - z_hi) / 2^31 % 2^1, z_hi * 2^1 + (v5_lo - z_lo) / 2^31 % 2^1 + k = row[11] * 2 + z = v1_lo % 2^32 + v6_lo % 2^32 + W[k-1] + v1_lo = z % 2^32 + v1_hi = v1_hi + v6_hi + (z - v1_lo) / 2^32 + W[k] + vC_lo, vC_hi = XOR(vC_hi, v1_hi), XOR(vC_lo, v1_lo) + z = vB_lo % 2^32 + vC_lo % 2^32 + vB_lo = z % 2^32 + vB_hi = vB_hi + vC_hi + (z - vB_lo) / 2^32 + v6_lo, v6_hi = XOR(v6_lo, vB_lo), XOR(v6_hi, vB_hi) + z_lo, z_hi = v6_lo % 2^24, v6_hi % 2^24 + v6_lo, v6_hi = (v6_lo - z_lo) / 2^24 % 2^8 + z_hi * 2^8, (v6_hi - z_hi) / 2^24 % 2^8 + z_lo * 2^8 + k = row[12] * 2 + z = v1_lo % 2^32 + v6_lo % 2^32 + W[k-1] + v1_lo = z % 2^32 + v1_hi = v1_hi + v6_hi + (z - v1_lo) / 2^32 + W[k] + vC_lo, vC_hi = XOR(vC_lo, v1_lo), XOR(vC_hi, v1_hi) + z_lo, z_hi = vC_lo % 2^16, vC_hi % 2^16 + vC_lo, vC_hi = (vC_lo - z_lo) / 2^16 % 2^16 + z_hi * 2^16, (vC_hi - z_hi) / 2^16 % 2^16 + z_lo * 2^16 + z = vB_lo % 2^32 + vC_lo % 2^32 + vB_lo = z % 2^32 + vB_hi = vB_hi + vC_hi + (z - vB_lo) / 2^32 + v6_lo, v6_hi = XOR(v6_lo, vB_lo), XOR(v6_hi, vB_hi) + z_lo, z_hi = v6_lo % 2^31, v6_hi % 2^31 + v6_lo, v6_hi = z_lo * 2^1 + (v6_hi - z_hi) / 2^31 % 2^1, z_hi * 2^1 + (v6_lo - z_lo) / 2^31 % 2^1 + k = row[13] * 2 + z = v2_lo % 2^32 + v7_lo % 2^32 + W[k-1] + v2_lo = z % 2^32 + v2_hi = v2_hi + v7_hi + (z - v2_lo) / 2^32 + W[k] + vD_lo, vD_hi = XOR(vD_hi, v2_hi), XOR(vD_lo, v2_lo) + z = v8_lo % 2^32 + vD_lo % 2^32 + v8_lo = z % 2^32 + v8_hi = v8_hi + vD_hi + (z - v8_lo) / 2^32 + v7_lo, v7_hi = XOR(v7_lo, v8_lo), XOR(v7_hi, v8_hi) + z_lo, z_hi = v7_lo % 2^24, v7_hi % 2^24 + v7_lo, v7_hi = (v7_lo - z_lo) / 2^24 % 2^8 + z_hi * 2^8, (v7_hi - z_hi) / 2^24 % 2^8 + z_lo * 2^8 + k = row[14] * 2 + z = v2_lo % 2^32 + v7_lo % 2^32 + W[k-1] + v2_lo = z % 2^32 + v2_hi = v2_hi + v7_hi + (z - v2_lo) / 2^32 + W[k] + vD_lo, vD_hi = XOR(vD_lo, v2_lo), XOR(vD_hi, v2_hi) + z_lo, z_hi = vD_lo % 2^16, vD_hi % 2^16 + vD_lo, vD_hi = (vD_lo - z_lo) / 2^16 % 2^16 + z_hi * 2^16, (vD_hi - z_hi) / 2^16 % 2^16 + z_lo * 2^16 + z = v8_lo % 2^32 + vD_lo % 2^32 + v8_lo = z % 2^32 + v8_hi = v8_hi + vD_hi + (z - v8_lo) / 2^32 + v7_lo, v7_hi = XOR(v7_lo, v8_lo), XOR(v7_hi, v8_hi) + z_lo, z_hi = v7_lo % 2^31, v7_hi % 2^31 + v7_lo, v7_hi = z_lo * 2^1 + (v7_hi - z_hi) / 2^31 % 2^1, z_hi * 2^1 + (v7_lo - z_lo) / 2^31 % 2^1 + k = row[15] * 2 + z = v3_lo % 2^32 + v4_lo % 2^32 + W[k-1] + v3_lo = z % 2^32 + v3_hi = v3_hi + v4_hi + (z - v3_lo) / 2^32 + W[k] + vE_lo, vE_hi = XOR(vE_hi, v3_hi), XOR(vE_lo, v3_lo) + z = v9_lo % 2^32 + vE_lo % 2^32 + v9_lo = z % 2^32 + v9_hi = v9_hi + vE_hi + (z - v9_lo) / 2^32 + v4_lo, v4_hi = XOR(v4_lo, v9_lo), XOR(v4_hi, v9_hi) + z_lo, z_hi = v4_lo % 2^24, v4_hi % 2^24 + v4_lo, v4_hi = (v4_lo - z_lo) / 2^24 % 2^8 + z_hi * 2^8, (v4_hi - z_hi) / 2^24 % 2^8 + z_lo * 2^8 + k = row[16] * 2 + z = v3_lo % 2^32 + v4_lo % 2^32 + W[k-1] + v3_lo = z % 2^32 + v3_hi = v3_hi + v4_hi + (z - v3_lo) / 2^32 + W[k] + vE_lo, vE_hi = XOR(vE_lo, v3_lo), XOR(vE_hi, v3_hi) + z_lo, z_hi = vE_lo % 2^16, vE_hi % 2^16 + vE_lo, vE_hi = (vE_lo - z_lo) / 2^16 % 2^16 + z_hi * 2^16, (vE_hi - z_hi) / 2^16 % 2^16 + z_lo * 2^16 + z = v9_lo % 2^32 + vE_lo % 2^32 + v9_lo = z % 2^32 + v9_hi = v9_hi + vE_hi + (z - v9_lo) / 2^32 + v4_lo, v4_hi = XOR(v4_lo, v9_lo), XOR(v4_hi, v9_hi) + z_lo, z_hi = v4_lo % 2^31, v4_hi % 2^31 + v4_lo, v4_hi = z_lo * 2^1 + (v4_hi - z_hi) / 2^31 % 2^1, z_hi * 2^1 + (v4_lo - z_lo) / 2^31 % 2^1 + end + h1_lo = XOR(h1_lo, v0_lo, v8_lo) % 2^32 + h2_lo = XOR(h2_lo, v1_lo, v9_lo) % 2^32 + h3_lo = XOR(h3_lo, v2_lo, vA_lo) % 2^32 + h4_lo = XOR(h4_lo, v3_lo, vB_lo) % 2^32 + h5_lo = XOR(h5_lo, v4_lo, vC_lo) % 2^32 + h6_lo = XOR(h6_lo, v5_lo, vD_lo) % 2^32 + h7_lo = XOR(h7_lo, v6_lo, vE_lo) % 2^32 + h8_lo = XOR(h8_lo, v7_lo, vF_lo) % 2^32 + h1_hi = XOR(h1_hi, v0_hi, v8_hi) % 2^32 + h2_hi = XOR(h2_hi, v1_hi, v9_hi) % 2^32 + h3_hi = XOR(h3_hi, v2_hi, vA_hi) % 2^32 + h4_hi = XOR(h4_hi, v3_hi, vB_hi) % 2^32 + h5_hi = XOR(h5_hi, v4_hi, vC_hi) % 2^32 + h6_hi = XOR(h6_hi, v5_hi, vD_hi) % 2^32 + h7_hi = XOR(h7_hi, v6_hi, vE_hi) % 2^32 + h8_hi = XOR(h8_hi, v7_hi, vF_hi) % 2^32 + end + H_lo[1], H_lo[2], H_lo[3], H_lo[4], H_lo[5], H_lo[6], H_lo[7], H_lo[8] = h1_lo, h2_lo, h3_lo, h4_lo, h5_lo, h6_lo, h7_lo, h8_lo + H_hi[1], H_hi[2], H_hi[3], H_hi[4], H_hi[5], H_hi[6], H_hi[7], H_hi[8] = h1_hi, h2_hi, h3_hi, h4_hi, h5_hi, h6_hi, h7_hi, h8_hi + return bytes_compressed + end + + + function blake3_feed_64(str, offs, size, flags, chunk_index, H_in, H_out, wide_output, block_length) + -- offs >= 0, size >= 0, size is multiple of 64 + block_length = block_length or 64 + local W = common_W + local h1, h2, h3, h4, h5, h6, h7, h8 = H_in[1], H_in[2], H_in[3], H_in[4], H_in[5], H_in[6], H_in[7], H_in[8] + H_out = H_out or H_in + for pos = offs, offs + size - 1, 64 do + if str then + for j = 1, 16 do + pos = pos + 4 + local a, b, c, d = byte(str, pos - 3, pos) + W[j] = ((d * 256 + c) * 256 + b) * 256 + a + end + end + local v0, v1, v2, v3, v4, v5, v6, v7 = h1, h2, h3, h4, h5, h6, h7, h8 + local v8, v9, vA, vB = sha2_H_hi[1], sha2_H_hi[2], sha2_H_hi[3], sha2_H_hi[4] + local vC = chunk_index % 2^32 -- t0 = low_4_bytes(chunk_index) + local vD = (chunk_index - vC) / 2^32 -- t1 = high_4_bytes(chunk_index) + local vE, vF = block_length, flags + for j = 1, 7 do + v0 = v0 + v4 + W[perm_blake3[j]] + vC = XOR(vC, v0) % 2^32 / 2^16 + vC = vC % 1 * (2^32 - 1) + vC + v8 = v8 + vC + v4 = XOR(v4, v8) % 2^32 / 2^12 + v4 = v4 % 1 * (2^32 - 1) + v4 + v0 = v0 + v4 + W[perm_blake3[j + 14]] + vC = XOR(vC, v0) % 2^32 / 2^8 + vC = vC % 1 * (2^32 - 1) + vC + v8 = v8 + vC + v4 = XOR(v4, v8) % 2^32 / 2^7 + v4 = v4 % 1 * (2^32 - 1) + v4 + v1 = v1 + v5 + W[perm_blake3[j + 1]] + vD = XOR(vD, v1) % 2^32 / 2^16 + vD = vD % 1 * (2^32 - 1) + vD + v9 = v9 + vD + v5 = XOR(v5, v9) % 2^32 / 2^12 + v5 = v5 % 1 * (2^32 - 1) + v5 + v1 = v1 + v5 + W[perm_blake3[j + 2]] + vD = XOR(vD, v1) % 2^32 / 2^8 + vD = vD % 1 * (2^32 - 1) + vD + v9 = v9 + vD + v5 = XOR(v5, v9) % 2^32 / 2^7 + v5 = v5 % 1 * (2^32 - 1) + v5 + v2 = v2 + v6 + W[perm_blake3[j + 16]] + vE = XOR(vE, v2) % 2^32 / 2^16 + vE = vE % 1 * (2^32 - 1) + vE + vA = vA + vE + v6 = XOR(v6, vA) % 2^32 / 2^12 + v6 = v6 % 1 * (2^32 - 1) + v6 + v2 = v2 + v6 + W[perm_blake3[j + 7]] + vE = XOR(vE, v2) % 2^32 / 2^8 + vE = vE % 1 * (2^32 - 1) + vE + vA = vA + vE + v6 = XOR(v6, vA) % 2^32 / 2^7 + v6 = v6 % 1 * (2^32 - 1) + v6 + v3 = v3 + v7 + W[perm_blake3[j + 15]] + vF = XOR(vF, v3) % 2^32 / 2^16 + vF = vF % 1 * (2^32 - 1) + vF + vB = vB + vF + v7 = XOR(v7, vB) % 2^32 / 2^12 + v7 = v7 % 1 * (2^32 - 1) + v7 + v3 = v3 + v7 + W[perm_blake3[j + 17]] + vF = XOR(vF, v3) % 2^32 / 2^8 + vF = vF % 1 * (2^32 - 1) + vF + vB = vB + vF + v7 = XOR(v7, vB) % 2^32 / 2^7 + v7 = v7 % 1 * (2^32 - 1) + v7 + v0 = v0 + v5 + W[perm_blake3[j + 21]] + vF = XOR(vF, v0) % 2^32 / 2^16 + vF = vF % 1 * (2^32 - 1) + vF + vA = vA + vF + v5 = XOR(v5, vA) % 2^32 / 2^12 + v5 = v5 % 1 * (2^32 - 1) + v5 + v0 = v0 + v5 + W[perm_blake3[j + 5]] + vF = XOR(vF, v0) % 2^32 / 2^8 + vF = vF % 1 * (2^32 - 1) + vF + vA = vA + vF + v5 = XOR(v5, vA) % 2^32 / 2^7 + v5 = v5 % 1 * (2^32 - 1) + v5 + v1 = v1 + v6 + W[perm_blake3[j + 3]] + vC = XOR(vC, v1) % 2^32 / 2^16 + vC = vC % 1 * (2^32 - 1) + vC + vB = vB + vC + v6 = XOR(v6, vB) % 2^32 / 2^12 + v6 = v6 % 1 * (2^32 - 1) + v6 + v1 = v1 + v6 + W[perm_blake3[j + 6]] + vC = XOR(vC, v1) % 2^32 / 2^8 + vC = vC % 1 * (2^32 - 1) + vC + vB = vB + vC + v6 = XOR(v6, vB) % 2^32 / 2^7 + v6 = v6 % 1 * (2^32 - 1) + v6 + v2 = v2 + v7 + W[perm_blake3[j + 4]] + vD = XOR(vD, v2) % 2^32 / 2^16 + vD = vD % 1 * (2^32 - 1) + vD + v8 = v8 + vD + v7 = XOR(v7, v8) % 2^32 / 2^12 + v7 = v7 % 1 * (2^32 - 1) + v7 + v2 = v2 + v7 + W[perm_blake3[j + 18]] + vD = XOR(vD, v2) % 2^32 / 2^8 + vD = vD % 1 * (2^32 - 1) + vD + v8 = v8 + vD + v7 = XOR(v7, v8) % 2^32 / 2^7 + v7 = v7 % 1 * (2^32 - 1) + v7 + v3 = v3 + v4 + W[perm_blake3[j + 19]] + vE = XOR(vE, v3) % 2^32 / 2^16 + vE = vE % 1 * (2^32 - 1) + vE + v9 = v9 + vE + v4 = XOR(v4, v9) % 2^32 / 2^12 + v4 = v4 % 1 * (2^32 - 1) + v4 + v3 = v3 + v4 + W[perm_blake3[j + 20]] + vE = XOR(vE, v3) % 2^32 / 2^8 + vE = vE % 1 * (2^32 - 1) + vE + v9 = v9 + vE + v4 = XOR(v4, v9) % 2^32 / 2^7 + v4 = v4 % 1 * (2^32 - 1) + v4 + end + if wide_output then + H_out[ 9] = XOR(h1, v8) + H_out[10] = XOR(h2, v9) + H_out[11] = XOR(h3, vA) + H_out[12] = XOR(h4, vB) + H_out[13] = XOR(h5, vC) + H_out[14] = XOR(h6, vD) + H_out[15] = XOR(h7, vE) + H_out[16] = XOR(h8, vF) + end + h1 = XOR(v0, v8) + h2 = XOR(v1, v9) + h3 = XOR(v2, vA) + h4 = XOR(v3, vB) + h5 = XOR(v4, vC) + h6 = XOR(v5, vD) + h7 = XOR(v6, vE) + h8 = XOR(v7, vF) + end + H_out[1], H_out[2], H_out[3], H_out[4], H_out[5], H_out[6], H_out[7], H_out[8] = h1, h2, h3, h4, h5, h6, h7, h8 + end + +end + + +-------------------------------------------------------------------------------- +-- MAGIC NUMBERS CALCULATOR +-------------------------------------------------------------------------------- +-- Q: +-- Is 53-bit "double" math enough to calculate square roots and cube roots of primes with 64 correct bits after decimal point? +-- A: +-- Yes, 53-bit "double" arithmetic is enough. +-- We could obtain first 40 bits by direct calculation of p^(1/3) and next 40 bits by one step of Newton's method. + +do + local function mul(src1, src2, factor, result_length) + -- src1, src2 - long integers (arrays of digits in base 2^24) + -- factor - small integer + -- returns long integer result (src1 * src2 * factor) and its floating point approximation + local result, carry, value, weight = {}, 0.0, 0.0, 1.0 + for j = 1, result_length do + for k = math_max(1, j + 1 - #src2), math_min(j, #src1) do + carry = carry + factor * src1[k] * src2[j + 1 - k] -- "int32" is not enough for multiplication result, that's why "factor" must be of type "double" + end + local digit = carry % 2^24 + result[j] = floor(digit) + carry = (carry - digit) / 2^24 + value = value + digit * weight + weight = weight * 2^24 + end + return result, value + end + + local idx, step, p, one, sqrt_hi, sqrt_lo = 0, {4, 1, 2, -2, 2}, 4, {1}, sha2_H_hi, sha2_H_lo + repeat + p = p + step[p % 6] + local d = 1 + repeat + d = d + step[d % 6] + if d*d > p then -- next prime number is found + local root = p^(1/3) + local R = root * 2^40 + R = mul({R - R % 1}, one, 1.0, 2) + local _, delta = mul(R, mul(R, R, 1.0, 4), -1.0, 4) + local hi = R[2] % 65536 * 65536 + floor(R[1] / 256) + local lo = R[1] % 256 * 16777216 + floor(delta * (2^-56 / 3) * root / p) + if idx < 16 then + root = p^(1/2) + R = root * 2^40 + R = mul({R - R % 1}, one, 1.0, 2) + _, delta = mul(R, R, -1.0, 2) + local hi = R[2] % 65536 * 65536 + floor(R[1] / 256) + local lo = R[1] % 256 * 16777216 + floor(delta * 2^-17 / root) + local idx = idx % 8 + 1 + sha2_H_ext256[224][idx] = lo + sqrt_hi[idx], sqrt_lo[idx] = hi, lo + hi * hi_factor + if idx > 7 then + sqrt_hi, sqrt_lo = sha2_H_ext512_hi[384], sha2_H_ext512_lo[384] + end + end + idx = idx + 1 + sha2_K_hi[idx], sha2_K_lo[idx] = hi, lo % K_lo_modulo + hi * hi_factor + break + end + until p % d == 0 + until idx > 79 +end + +-- Calculating IVs for SHA512/224 and SHA512/256 +for width = 224, 256, 32 do + local H_lo, H_hi = {} + if HEX64 then + for j = 1, 8 do + H_lo[j] = XORA5(sha2_H_lo[j]) + end + else + H_hi = {} + for j = 1, 8 do + H_lo[j] = XORA5(sha2_H_lo[j]) + H_hi[j] = XORA5(sha2_H_hi[j]) + end + end + sha512_feed_128(H_lo, H_hi, "SHA-512/"..tostring(width).."\128"..string_rep("\0", 115).."\88", 0, 128) + sha2_H_ext512_lo[width] = H_lo + sha2_H_ext512_hi[width] = H_hi +end + +-- Constants for MD5 +do + local sin, abs, modf = math.sin, math.abs, math.modf + for idx = 1, 64 do + -- we can't use formula floor(abs(sin(idx))*2^32) because its result may be beyond integer range on Lua built with 32-bit integers + local hi, lo = modf(abs(sin(idx)) * 2^16) + md5_K[idx] = hi * 65536 + floor(lo * 2^16) + end +end + +-- Constants for SHA-3 +do + local sh_reg = 29 + + local function next_bit() + local r = sh_reg % 2 + sh_reg = XOR_BYTE((sh_reg - r) / 2, 142 * r) + return r + end + + for idx = 1, 24 do + local lo, m = 0 + for _ = 1, 6 do + m = m and m * m * 2 or 1 + lo = lo + next_bit() * m + end + local hi = next_bit() * m + sha3_RC_hi[idx], sha3_RC_lo[idx] = hi, lo + hi * hi_factor_keccak + end +end + +if branch == "FFI" then + sha2_K_hi = ffi.new("uint32_t[?]", #sha2_K_hi + 1, 0, unpack(sha2_K_hi)) + sha2_K_lo = ffi.new("int64_t[?]", #sha2_K_lo + 1, 0, unpack(sha2_K_lo)) + -- md5_K = ffi.new("uint32_t[?]", #md5_K + 1, 0, unpack(md5_K)) + if hi_factor_keccak == 0 then + sha3_RC_lo = ffi.new("uint32_t[?]", #sha3_RC_lo + 1, 0, unpack(sha3_RC_lo)) + sha3_RC_hi = ffi.new("uint32_t[?]", #sha3_RC_hi + 1, 0, unpack(sha3_RC_hi)) + else + sha3_RC_lo = ffi.new("int64_t[?]", #sha3_RC_lo + 1, 0, unpack(sha3_RC_lo)) + end +end + + +-------------------------------------------------------------------------------- +-- MAIN FUNCTIONS +-------------------------------------------------------------------------------- + +local function sha256ext(width, message) + -- Create an instance (private objects for current calculation) + local H, length, tail = {unpack(sha2_H_ext256[width])}, 0.0, "" + + local function partial(message_part) + if message_part then + if tail then + length = length + #message_part + local offs = 0 + if tail ~= "" and #tail + #message_part >= 64 then + offs = 64 - #tail + sha256_feed_64(H, tail..sub(message_part, 1, offs), 0, 64) + tail = "" + end + local size = #message_part - offs + local size_tail = size % 64 + sha256_feed_64(H, message_part, offs, size - size_tail) + tail = tail..sub(message_part, #message_part + 1 - size_tail) + return partial + else + error("Adding more chunks is not allowed after receiving the result", 2) + end + else + if tail then + local final_blocks = {tail, "\128", string_rep("\0", (-9 - length) % 64 + 1)} + tail = nil + -- Assuming user data length is shorter than (2^53)-9 bytes + -- Anyway, it looks very unrealistic that someone would spend more than a year of calculations to process 2^53 bytes of data by using this Lua script :-) + -- 2^53 bytes = 2^56 bits, so "bit-counter" fits in 7 bytes + length = length * (8 / 256^7) -- convert "byte-counter" to "bit-counter" and move decimal point to the left + for j = 4, 10 do + length = length % 1 * 256 + final_blocks[j] = char(floor(length)) + end + final_blocks = table_concat(final_blocks) + sha256_feed_64(H, final_blocks, 0, #final_blocks) + local max_reg = width / 32 + for j = 1, max_reg do + H[j] = HEX(H[j]) + end + H = table_concat(H, "", 1, max_reg) + end + return H + end + end + + if message then + -- Actually perform calculations and return the SHA256 digest of a message + return partial(message)() + else + -- Return function for chunk-by-chunk loading + -- User should feed every chunk of input data as single argument to this function and finally get SHA256 digest by invoking this function without an argument + return partial + end +end + + +local function sha512ext(width, message) + -- Create an instance (private objects for current calculation) + local length, tail, H_lo, H_hi = 0.0, "", {unpack(sha2_H_ext512_lo[width])}, not HEX64 and {unpack(sha2_H_ext512_hi[width])} + + local function partial(message_part) + if message_part then + if tail then + length = length + #message_part + local offs = 0 + if tail ~= "" and #tail + #message_part >= 128 then + offs = 128 - #tail + sha512_feed_128(H_lo, H_hi, tail..sub(message_part, 1, offs), 0, 128) + tail = "" + end + local size = #message_part - offs + local size_tail = size % 128 + sha512_feed_128(H_lo, H_hi, message_part, offs, size - size_tail) + tail = tail..sub(message_part, #message_part + 1 - size_tail) + return partial + else + error("Adding more chunks is not allowed after receiving the result", 2) + end + else + if tail then + local final_blocks = {tail, "\128", string_rep("\0", (-17-length) % 128 + 9)} + tail = nil + -- Assuming user data length is shorter than (2^53)-17 bytes + -- 2^53 bytes = 2^56 bits, so "bit-counter" fits in 7 bytes + length = length * (8 / 256^7) -- convert "byte-counter" to "bit-counter" and move floating point to the left + for j = 4, 10 do + length = length % 1 * 256 + final_blocks[j] = char(floor(length)) + end + final_blocks = table_concat(final_blocks) + sha512_feed_128(H_lo, H_hi, final_blocks, 0, #final_blocks) + local max_reg = ceil(width / 64) + if HEX64 then + for j = 1, max_reg do + H_lo[j] = HEX64(H_lo[j]) + end + else + for j = 1, max_reg do + H_lo[j] = HEX(H_hi[j])..HEX(H_lo[j]) + end + H_hi = nil + end + H_lo = sub(table_concat(H_lo, "", 1, max_reg), 1, width / 4) + end + return H_lo + end + end + + if message then + -- Actually perform calculations and return the SHA512 digest of a message + return partial(message)() + else + -- Return function for chunk-by-chunk loading + -- User should feed every chunk of input data as single argument to this function and finally get SHA512 digest by invoking this function without an argument + return partial + end +end + + +local function md5(message) + -- Create an instance (private objects for current calculation) + local H, length, tail = {unpack(md5_sha1_H, 1, 4)}, 0.0, "" + + local function partial(message_part) + if message_part then + if tail then + length = length + #message_part + local offs = 0 + if tail ~= "" and #tail + #message_part >= 64 then + offs = 64 - #tail + md5_feed_64(H, tail..sub(message_part, 1, offs), 0, 64) + tail = "" + end + local size = #message_part - offs + local size_tail = size % 64 + md5_feed_64(H, message_part, offs, size - size_tail) + tail = tail..sub(message_part, #message_part + 1 - size_tail) + return partial + else + error("Adding more chunks is not allowed after receiving the result", 2) + end + else + if tail then + local final_blocks = {tail, "\128", string_rep("\0", (-9 - length) % 64)} + tail = nil + length = length * 8 -- convert "byte-counter" to "bit-counter" + for j = 4, 11 do + local low_byte = length % 256 + final_blocks[j] = char(low_byte) + length = (length - low_byte) / 256 + end + final_blocks = table_concat(final_blocks) + md5_feed_64(H, final_blocks, 0, #final_blocks) + for j = 1, 4 do + H[j] = HEX(H[j]) + end + H = gsub(table_concat(H), "(..)(..)(..)(..)", "%4%3%2%1") + end + return H + end + end + + if message then + -- Actually perform calculations and return the MD5 digest of a message + return partial(message)() + else + -- Return function for chunk-by-chunk loading + -- User should feed every chunk of input data as single argument to this function and finally get MD5 digest by invoking this function without an argument + return partial + end +end + + +local function sha1(message) + -- Create an instance (private objects for current calculation) + local H, length, tail = {unpack(md5_sha1_H)}, 0.0, "" + + local function partial(message_part) + if message_part then + if tail then + length = length + #message_part + local offs = 0 + if tail ~= "" and #tail + #message_part >= 64 then + offs = 64 - #tail + sha1_feed_64(H, tail..sub(message_part, 1, offs), 0, 64) + tail = "" + end + local size = #message_part - offs + local size_tail = size % 64 + sha1_feed_64(H, message_part, offs, size - size_tail) + tail = tail..sub(message_part, #message_part + 1 - size_tail) + return partial + else + error("Adding more chunks is not allowed after receiving the result", 2) + end + else + if tail then + local final_blocks = {tail, "\128", string_rep("\0", (-9 - length) % 64 + 1)} + tail = nil + -- Assuming user data length is shorter than (2^53)-9 bytes + -- 2^53 bytes = 2^56 bits, so "bit-counter" fits in 7 bytes + length = length * (8 / 256^7) -- convert "byte-counter" to "bit-counter" and move decimal point to the left + for j = 4, 10 do + length = length % 1 * 256 + final_blocks[j] = char(floor(length)) + end + final_blocks = table_concat(final_blocks) + sha1_feed_64(H, final_blocks, 0, #final_blocks) + for j = 1, 5 do + H[j] = HEX(H[j]) + end + H = table_concat(H) + end + return H + end + end + + if message then + -- Actually perform calculations and return the SHA-1 digest of a message + return partial(message)() + else + -- Return function for chunk-by-chunk loading + -- User should feed every chunk of input data as single argument to this function and finally get SHA-1 digest by invoking this function without an argument + return partial + end +end + + +local function keccak(block_size_in_bytes, digest_size_in_bytes, is_SHAKE, message) + -- "block_size_in_bytes" is multiple of 8 + if type(digest_size_in_bytes) ~= "number" then + -- arguments in SHAKE are swapped: + -- NIST FIPS 202 defines SHAKE(message,num_bits) + -- this module defines SHAKE(num_bytes,message) + -- it's easy to forget about this swap, hence the check + error("Argument 'digest_size_in_bytes' must be a number", 2) + end + -- Create an instance (private objects for current calculation) + local tail, lanes_lo, lanes_hi = "", create_array_of_lanes(), hi_factor_keccak == 0 and create_array_of_lanes() + local result + + local function partial(message_part) + if message_part then + if tail then + local offs = 0 + if tail ~= "" and #tail + #message_part >= block_size_in_bytes then + offs = block_size_in_bytes - #tail + keccak_feed(lanes_lo, lanes_hi, tail..sub(message_part, 1, offs), 0, block_size_in_bytes, block_size_in_bytes) + tail = "" + end + local size = #message_part - offs + local size_tail = size % block_size_in_bytes + keccak_feed(lanes_lo, lanes_hi, message_part, offs, size - size_tail, block_size_in_bytes) + tail = tail..sub(message_part, #message_part + 1 - size_tail) + return partial + else + error("Adding more chunks is not allowed after receiving the result", 2) + end + else + if tail then + -- append the following bits to the message: for usual SHA-3: 011(0*)1, for SHAKE: 11111(0*)1 + local gap_start = is_SHAKE and 31 or 6 + tail = tail..(#tail + 1 == block_size_in_bytes and char(gap_start + 128) or char(gap_start)..string_rep("\0", (-2 - #tail) % block_size_in_bytes).."\128") + keccak_feed(lanes_lo, lanes_hi, tail, 0, #tail, block_size_in_bytes) + tail = nil + local lanes_used = 0 + local total_lanes = floor(block_size_in_bytes / 8) + local qwords = {} + + local function get_next_qwords_of_digest(qwords_qty) + -- returns not more than 'qwords_qty' qwords ('qwords_qty' might be non-integer) + -- doesn't go across keccak-buffer boundary + -- block_size_in_bytes is a multiple of 8, so, keccak-buffer contains integer number of qwords + if lanes_used >= total_lanes then + keccak_feed(lanes_lo, lanes_hi, "\0\0\0\0\0\0\0\0", 0, 8, 8) + lanes_used = 0 + end + qwords_qty = floor(math_min(qwords_qty, total_lanes - lanes_used)) + if hi_factor_keccak ~= 0 then + for j = 1, qwords_qty do + qwords[j] = HEX64(lanes_lo[lanes_used + j - 1 + lanes_index_base]) + end + else + for j = 1, qwords_qty do + qwords[j] = HEX(lanes_hi[lanes_used + j])..HEX(lanes_lo[lanes_used + j]) + end + end + lanes_used = lanes_used + qwords_qty + return + gsub(table_concat(qwords, "", 1, qwords_qty), "(..)(..)(..)(..)(..)(..)(..)(..)", "%8%7%6%5%4%3%2%1"), + qwords_qty * 8 + end + + local parts = {} -- digest parts + local last_part, last_part_size = "", 0 + + local function get_next_part_of_digest(bytes_needed) + -- returns 'bytes_needed' bytes, for arbitrary integer 'bytes_needed' + bytes_needed = bytes_needed or 1 + if bytes_needed <= last_part_size then + last_part_size = last_part_size - bytes_needed + local part_size_in_nibbles = bytes_needed * 2 + local result = sub(last_part, 1, part_size_in_nibbles) + last_part = sub(last_part, part_size_in_nibbles + 1) + return result + end + local parts_qty = 0 + if last_part_size > 0 then + parts_qty = 1 + parts[parts_qty] = last_part + bytes_needed = bytes_needed - last_part_size + end + -- repeats until the length is enough + while bytes_needed >= 8 do + local next_part, next_part_size = get_next_qwords_of_digest(bytes_needed / 8) + parts_qty = parts_qty + 1 + parts[parts_qty] = next_part + bytes_needed = bytes_needed - next_part_size + end + if bytes_needed > 0 then + last_part, last_part_size = get_next_qwords_of_digest(1) + parts_qty = parts_qty + 1 + parts[parts_qty] = get_next_part_of_digest(bytes_needed) + else + last_part, last_part_size = "", 0 + end + return table_concat(parts, "", 1, parts_qty) + end + + if digest_size_in_bytes < 0 then + result = get_next_part_of_digest + else + result = get_next_part_of_digest(digest_size_in_bytes) + end + end + return result + end + end + + if message then + -- Actually perform calculations and return the SHA-3 digest of a message + return partial(message)() + else + -- Return function for chunk-by-chunk loading + -- User should feed every chunk of input data as single argument to this function and finally get SHA-3 digest by invoking this function without an argument + return partial + end +end + + +local hex_to_bin, bin_to_hex, bin_to_base64, base64_to_bin +do + local function repl_hex2bin(hh) + return char(tonumber(hh, 16)) + end + function hex_to_bin(hex_string) + return (gsub(hex_string, "%x%x", repl_hex2bin)) + end + + local function repl_bin2hex(c) + return string_format("%02x", byte(c)) + end + function bin_to_hex(binary_string) + return (gsub(binary_string, ".", repl_bin2hex)) + end + + local base64_symbols = { + ['+'] = 62, ['-'] = 62, [62] = '+', + ['/'] = 63, ['_'] = 63, [63] = '/', + ['='] = -1, ['.'] = -1, [-1] = '=' + } + local symbol_index = 0 + for j, pair in ipairs{'AZ', 'az', '09'} do + for ascii = byte(pair), byte(pair, 2) do + local ch = char(ascii) + base64_symbols[ch] = symbol_index + base64_symbols[symbol_index] = ch + symbol_index = symbol_index + 1 + end + end + + function bin_to_base64(binary_string) + local result = {} + for pos = 1, #binary_string, 3 do + local c1, c2, c3, c4 = byte(sub(binary_string, pos, pos + 2)..'\0', 1, -1) + result[#result + 1] = + base64_symbols[floor(c1 / 4)] + ..base64_symbols[c1 % 4 * 16 + floor(c2 / 16)] + ..base64_symbols[c3 and c2 % 16 * 4 + floor(c3 / 64) or -1] + ..base64_symbols[c4 and c3 % 64 or -1] + end + return table_concat(result) + end + + function base64_to_bin(base64_string) + local result, chars_qty = {}, 3 + for pos, ch in gmatch(gsub(base64_string, '%s+', ''), '()(.)') do + local code = base64_symbols[ch] + if code < 0 then + chars_qty = chars_qty - 1 + code = 0 + end + local idx = pos % 4 + if idx > 0 then + result[-idx] = code + else + local c1 = result[-1] * 4 + floor(result[-2] / 16) + local c2 = (result[-2] % 16) * 16 + floor(result[-3] / 4) + local c3 = (result[-3] % 4) * 64 + code + result[#result + 1] = sub(char(c1, c2, c3), 1, chars_qty) + end + end + return table_concat(result) + end + +end + + +local block_size_for_HMAC -- this table will be initialized at the end of the module + +local function pad_and_xor(str, result_length, byte_for_xor) + return gsub(str, ".", + function(c) + return char(XOR_BYTE(byte(c), byte_for_xor)) + end + )..string_rep(char(byte_for_xor), result_length - #str) +end + +local function hmac(hash_func, key, message) + -- Create an instance (private objects for current calculation) + local block_size = block_size_for_HMAC[hash_func] + if not block_size then + error("Unknown hash function", 2) + end + if #key > block_size then + key = hex_to_bin(hash_func(key)) + end + local append = hash_func()(pad_and_xor(key, block_size, 0x36)) + local result + + local function partial(message_part) + if not message_part then + result = result or hash_func(pad_and_xor(key, block_size, 0x5C)..hex_to_bin(append())) + return result + elseif result then + error("Adding more chunks is not allowed after receiving the result", 2) + else + append(message_part) + return partial + end + end + + if message then + -- Actually perform calculations and return the HMAC of a message + return partial(message)() + else + -- Return function for chunk-by-chunk loading of a message + -- User should feed every chunk of the message as single argument to this function and finally get HMAC by invoking this function without an argument + return partial + end +end + + +local function xor_blake2_salt(salt, letter, H_lo, H_hi) + -- salt: concatenation of "Salt"+"Personalization" fields + local max_size = letter == "s" and 16 or 32 + local salt_size = #salt + if salt_size > max_size then + error(string_format("For BLAKE2%s/BLAKE2%sp/BLAKE2X%s the 'salt' parameter length must not exceed %d bytes", letter, letter, letter, max_size), 2) + end + if H_lo then + local offset, blake2_word_size, xor = 0, letter == "s" and 4 or 8, letter == "s" and XOR or XORA5 + for j = 5, 4 + ceil(salt_size / blake2_word_size) do + local prev, last + for _ = 1, blake2_word_size, 4 do + offset = offset + 4 + local a, b, c, d = byte(salt, offset - 3, offset) + local four_bytes = (((d or 0) * 256 + (c or 0)) * 256 + (b or 0)) * 256 + (a or 0) + prev, last = last, four_bytes + end + H_lo[j] = xor(H_lo[j], prev and last * hi_factor + prev or last) + if H_hi then + H_hi[j] = xor(H_hi[j], last) + end + end + end +end + +local function blake2s(message, key, salt, digest_size_in_bytes, XOF_length, B2_offset) + -- message: binary string to be hashed (or nil for "chunk-by-chunk" input mode) + -- key: (optional) binary string up to 32 bytes, by default empty string + -- salt: (optional) binary string up to 16 bytes, by default empty string + -- digest_size_in_bytes: (optional) integer from 1 to 32, by default 32 + -- The last two parameters "XOF_length" and "B2_offset" are for internal use only, user must omit them (or pass nil) + digest_size_in_bytes = digest_size_in_bytes or 32 + if digest_size_in_bytes < 1 or digest_size_in_bytes > 32 then + error("BLAKE2s digest length must be from 1 to 32 bytes", 2) + end + key = key or "" + local key_length = #key + if key_length > 32 then + error("BLAKE2s key length must not exceed 32 bytes", 2) + end + salt = salt or "" + local bytes_compressed, tail, H = 0.0, "", {unpack(sha2_H_hi)} + if B2_offset then + H[1] = XOR(H[1], digest_size_in_bytes) + H[2] = XOR(H[2], 0x20) + H[3] = XOR(H[3], B2_offset) + H[4] = XOR(H[4], 0x20000000 + XOF_length) + else + H[1] = XOR(H[1], 0x01010000 + key_length * 256 + digest_size_in_bytes) + if XOF_length then + H[4] = XOR(H[4], XOF_length) + end + end + if salt ~= "" then + xor_blake2_salt(salt, "s", H) + end + + local function partial(message_part) + if message_part then + if tail then + local offs = 0 + if tail ~= "" and #tail + #message_part > 64 then + offs = 64 - #tail + bytes_compressed = blake2s_feed_64(H, tail..sub(message_part, 1, offs), 0, 64, bytes_compressed) + tail = "" + end + local size = #message_part - offs + local size_tail = size > 0 and (size - 1) % 64 + 1 or 0 + bytes_compressed = blake2s_feed_64(H, message_part, offs, size - size_tail, bytes_compressed) + tail = tail..sub(message_part, #message_part + 1 - size_tail) + return partial + else + error("Adding more chunks is not allowed after receiving the result", 2) + end + else + if tail then + if B2_offset then + blake2s_feed_64(H, nil, 0, 64, 0, 32) + else + blake2s_feed_64(H, tail..string_rep("\0", 64 - #tail), 0, 64, bytes_compressed, #tail) + end + tail = nil + if not XOF_length or B2_offset then + local max_reg = ceil(digest_size_in_bytes / 4) + for j = 1, max_reg do + H[j] = HEX(H[j]) + end + H = sub(gsub(table_concat(H, "", 1, max_reg), "(..)(..)(..)(..)", "%4%3%2%1"), 1, digest_size_in_bytes * 2) + end + end + return H + end + end + + if key_length > 0 then + partial(key..string_rep("\0", 64 - key_length)) + end + if B2_offset then + return partial() + elseif message then + -- Actually perform calculations and return the BLAKE2s digest of a message + return partial(message)() + else + -- Return function for chunk-by-chunk loading + -- User should feed every chunk of input data as single argument to this function and finally get BLAKE2s digest by invoking this function without an argument + return partial + end +end + +local function blake2b(message, key, salt, digest_size_in_bytes, XOF_length, B2_offset) + -- message: binary string to be hashed (or nil for "chunk-by-chunk" input mode) + -- key: (optional) binary string up to 64 bytes, by default empty string + -- salt: (optional) binary string up to 32 bytes, by default empty string + -- digest_size_in_bytes: (optional) integer from 1 to 64, by default 64 + -- The last two parameters "XOF_length" and "B2_offset" are for internal use only, user must omit them (or pass nil) + digest_size_in_bytes = floor(digest_size_in_bytes or 64) + if digest_size_in_bytes < 1 or digest_size_in_bytes > 64 then + error("BLAKE2b digest length must be from 1 to 64 bytes", 2) + end + key = key or "" + local key_length = #key + if key_length > 64 then + error("BLAKE2b key length must not exceed 64 bytes", 2) + end + salt = salt or "" + local bytes_compressed, tail, H_lo, H_hi = 0.0, "", {unpack(sha2_H_lo)}, not HEX64 and {unpack(sha2_H_hi)} + if B2_offset then + if H_hi then + H_lo[1] = XORA5(H_lo[1], digest_size_in_bytes) + H_hi[1] = XORA5(H_hi[1], 0x40) + H_lo[2] = XORA5(H_lo[2], B2_offset) + H_hi[2] = XORA5(H_hi[2], XOF_length) + else + H_lo[1] = XORA5(H_lo[1], 0x40 * hi_factor + digest_size_in_bytes) + H_lo[2] = XORA5(H_lo[2], XOF_length * hi_factor + B2_offset) + end + H_lo[3] = XORA5(H_lo[3], 0x4000) + else + H_lo[1] = XORA5(H_lo[1], 0x01010000 + key_length * 256 + digest_size_in_bytes) + if XOF_length then + if H_hi then + H_hi[2] = XORA5(H_hi[2], XOF_length) + else + H_lo[2] = XORA5(H_lo[2], XOF_length * hi_factor) + end + end + end + if salt ~= "" then + xor_blake2_salt(salt, "b", H_lo, H_hi) + end + + local function partial(message_part) + if message_part then + if tail then + local offs = 0 + if tail ~= "" and #tail + #message_part > 128 then + offs = 128 - #tail + bytes_compressed = blake2b_feed_128(H_lo, H_hi, tail..sub(message_part, 1, offs), 0, 128, bytes_compressed) + tail = "" + end + local size = #message_part - offs + local size_tail = size > 0 and (size - 1) % 128 + 1 or 0 + bytes_compressed = blake2b_feed_128(H_lo, H_hi, message_part, offs, size - size_tail, bytes_compressed) + tail = tail..sub(message_part, #message_part + 1 - size_tail) + return partial + else + error("Adding more chunks is not allowed after receiving the result", 2) + end + else + if tail then + if B2_offset then + blake2b_feed_128(H_lo, H_hi, nil, 0, 128, 0, 64) + else + blake2b_feed_128(H_lo, H_hi, tail..string_rep("\0", 128 - #tail), 0, 128, bytes_compressed, #tail) + end + tail = nil + if XOF_length and not B2_offset then + if H_hi then + for j = 8, 1, -1 do + H_lo[j*2] = H_hi[j] + H_lo[j*2-1] = H_lo[j] + end + return H_lo, 16 + end + else + local max_reg = ceil(digest_size_in_bytes / 8) + if H_hi then + for j = 1, max_reg do + H_lo[j] = HEX(H_hi[j])..HEX(H_lo[j]) + end + else + for j = 1, max_reg do + H_lo[j] = HEX64(H_lo[j]) + end + end + H_lo = sub(gsub(table_concat(H_lo, "", 1, max_reg), "(..)(..)(..)(..)(..)(..)(..)(..)", "%8%7%6%5%4%3%2%1"), 1, digest_size_in_bytes * 2) + end + H_hi = nil + end + return H_lo + end + end + + if key_length > 0 then + partial(key..string_rep("\0", 128 - key_length)) + end + if B2_offset then + return partial() + elseif message then + -- Actually perform calculations and return the BLAKE2b digest of a message + return partial(message)() + else + -- Return function for chunk-by-chunk loading + -- User should feed every chunk of input data as single argument to this function and finally get BLAKE2b digest by invoking this function without an argument + return partial + end +end + +local function blake2sp(message, key, salt, digest_size_in_bytes) + -- message: binary string to be hashed (or nil for "chunk-by-chunk" input mode) + -- key: (optional) binary string up to 32 bytes, by default empty string + -- salt: (optional) binary string up to 16 bytes, by default empty string + -- digest_size_in_bytes: (optional) integer from 1 to 32, by default 32 + digest_size_in_bytes = digest_size_in_bytes or 32 + if digest_size_in_bytes < 1 or digest_size_in_bytes > 32 then + error("BLAKE2sp digest length must be from 1 to 32 bytes", 2) + end + key = key or "" + local key_length = #key + if key_length > 32 then + error("BLAKE2sp key length must not exceed 32 bytes", 2) + end + salt = salt or "" + local instances, length, first_dword_of_parameter_block, result = {}, 0.0, 0x02080000 + key_length * 256 + digest_size_in_bytes + for j = 1, 8 do + local bytes_compressed, tail, H = 0.0, "", {unpack(sha2_H_hi)} + instances[j] = {bytes_compressed, tail, H} + H[1] = XOR(H[1], first_dword_of_parameter_block) + H[3] = XOR(H[3], j-1) + H[4] = XOR(H[4], 0x20000000) + if salt ~= "" then + xor_blake2_salt(salt, "s", H) + end + end + + local function partial(message_part) + if message_part then + if instances then + local from = 0 + while true do + local to = math_min(from + 64 - length % 64, #message_part) + if to > from then + local inst = instances[floor(length / 64) % 8 + 1] + local part = sub(message_part, from + 1, to) + length, from = length + to - from, to + local bytes_compressed, tail = inst[1], inst[2] + if #tail < 64 then + tail = tail..part + else + local H = inst[3] + bytes_compressed = blake2s_feed_64(H, tail, 0, 64, bytes_compressed) + tail = part + end + inst[1], inst[2] = bytes_compressed, tail + else + break + end + end + return partial + else + error("Adding more chunks is not allowed after receiving the result", 2) + end + else + if instances then + local root_H = {unpack(sha2_H_hi)} + root_H[1] = XOR(root_H[1], first_dword_of_parameter_block) + root_H[4] = XOR(root_H[4], 0x20010000) + if salt ~= "" then + xor_blake2_salt(salt, "s", root_H) + end + for j = 1, 8 do + local inst = instances[j] + local bytes_compressed, tail, H = inst[1], inst[2], inst[3] + blake2s_feed_64(H, tail..string_rep("\0", 64 - #tail), 0, 64, bytes_compressed, #tail, j == 8) + if j % 2 == 0 then + local index = 0 + for k = j - 1, j do + local inst = instances[k] + local H = inst[3] + for i = 1, 8 do + index = index + 1 + common_W_blake2s[index] = H[i] + end + end + blake2s_feed_64(root_H, nil, 0, 64, 64 * (j/2 - 1), j == 8 and 64, j == 8) + end + end + instances = nil + local max_reg = ceil(digest_size_in_bytes / 4) + for j = 1, max_reg do + root_H[j] = HEX(root_H[j]) + end + result = sub(gsub(table_concat(root_H, "", 1, max_reg), "(..)(..)(..)(..)", "%4%3%2%1"), 1, digest_size_in_bytes * 2) + end + return result + end + end + + if key_length > 0 then + key = key..string_rep("\0", 64 - key_length) + for j = 1, 8 do + partial(key) + end + end + if message then + -- Actually perform calculations and return the BLAKE2sp digest of a message + return partial(message)() + else + -- Return function for chunk-by-chunk loading + -- User should feed every chunk of input data as single argument to this function and finally get BLAKE2sp digest by invoking this function without an argument + return partial + end + +end + +local function blake2bp(message, key, salt, digest_size_in_bytes) + -- message: binary string to be hashed (or nil for "chunk-by-chunk" input mode) + -- key: (optional) binary string up to 64 bytes, by default empty string + -- salt: (optional) binary string up to 32 bytes, by default empty string + -- digest_size_in_bytes: (optional) integer from 1 to 64, by default 64 + digest_size_in_bytes = digest_size_in_bytes or 64 + if digest_size_in_bytes < 1 or digest_size_in_bytes > 64 then + error("BLAKE2bp digest length must be from 1 to 64 bytes", 2) + end + key = key or "" + local key_length = #key + if key_length > 64 then + error("BLAKE2bp key length must not exceed 64 bytes", 2) + end + salt = salt or "" + local instances, length, first_dword_of_parameter_block, result = {}, 0.0, 0x02040000 + key_length * 256 + digest_size_in_bytes + for j = 1, 4 do + local bytes_compressed, tail, H_lo, H_hi = 0.0, "", {unpack(sha2_H_lo)}, not HEX64 and {unpack(sha2_H_hi)} + instances[j] = {bytes_compressed, tail, H_lo, H_hi} + H_lo[1] = XORA5(H_lo[1], first_dword_of_parameter_block) + H_lo[2] = XORA5(H_lo[2], j-1) + H_lo[3] = XORA5(H_lo[3], 0x4000) + if salt ~= "" then + xor_blake2_salt(salt, "b", H_lo, H_hi) + end + end + + local function partial(message_part) + if message_part then + if instances then + local from = 0 + while true do + local to = math_min(from + 128 - length % 128, #message_part) + if to > from then + local inst = instances[floor(length / 128) % 4 + 1] + local part = sub(message_part, from + 1, to) + length, from = length + to - from, to + local bytes_compressed, tail = inst[1], inst[2] + if #tail < 128 then + tail = tail..part + else + local H_lo, H_hi = inst[3], inst[4] + bytes_compressed = blake2b_feed_128(H_lo, H_hi, tail, 0, 128, bytes_compressed) + tail = part + end + inst[1], inst[2] = bytes_compressed, tail + else + break + end + end + return partial + else + error("Adding more chunks is not allowed after receiving the result", 2) + end + else + if instances then + local root_H_lo, root_H_hi = {unpack(sha2_H_lo)}, not HEX64 and {unpack(sha2_H_hi)} + root_H_lo[1] = XORA5(root_H_lo[1], first_dword_of_parameter_block) + root_H_lo[3] = XORA5(root_H_lo[3], 0x4001) + if salt ~= "" then + xor_blake2_salt(salt, "b", root_H_lo, root_H_hi) + end + for j = 1, 4 do + local inst = instances[j] + local bytes_compressed, tail, H_lo, H_hi = inst[1], inst[2], inst[3], inst[4] + blake2b_feed_128(H_lo, H_hi, tail..string_rep("\0", 128 - #tail), 0, 128, bytes_compressed, #tail, j == 4) + if j % 2 == 0 then + local index = 0 + for k = j - 1, j do + local inst = instances[k] + local H_lo, H_hi = inst[3], inst[4] + for i = 1, 8 do + index = index + 1 + common_W_blake2b[index] = H_lo[i] + if H_hi then + index = index + 1 + common_W_blake2b[index] = H_hi[i] + end + end + end + blake2b_feed_128(root_H_lo, root_H_hi, nil, 0, 128, 128 * (j/2 - 1), j == 4 and 128, j == 4) + end + end + instances = nil + local max_reg = ceil(digest_size_in_bytes / 8) + if HEX64 then + for j = 1, max_reg do + root_H_lo[j] = HEX64(root_H_lo[j]) + end + else + for j = 1, max_reg do + root_H_lo[j] = HEX(root_H_hi[j])..HEX(root_H_lo[j]) + end + end + result = sub(gsub(table_concat(root_H_lo, "", 1, max_reg), "(..)(..)(..)(..)(..)(..)(..)(..)", "%8%7%6%5%4%3%2%1"), 1, digest_size_in_bytes * 2) + end + return result + end + end + + if key_length > 0 then + key = key..string_rep("\0", 128 - key_length) + for j = 1, 4 do + partial(key) + end + end + if message then + -- Actually perform calculations and return the BLAKE2bp digest of a message + return partial(message)() + else + -- Return function for chunk-by-chunk loading + -- User should feed every chunk of input data as single argument to this function and finally get BLAKE2bp digest by invoking this function without an argument + return partial + end + +end + +local function blake2x(inner_func, inner_func_letter, common_W_blake2, block_size, digest_size_in_bytes, message, key, salt) + local XOF_digest_length_limit, XOF_digest_length, chunk_by_chunk_output = 2^(block_size / 2) - 1 + if digest_size_in_bytes == -1 then -- infinite digest + digest_size_in_bytes = math_huge + XOF_digest_length = floor(XOF_digest_length_limit) + chunk_by_chunk_output = true + else + if digest_size_in_bytes < 0 then + digest_size_in_bytes = -1.0 * digest_size_in_bytes + chunk_by_chunk_output = true + end + XOF_digest_length = floor(digest_size_in_bytes) + if XOF_digest_length >= XOF_digest_length_limit then + error("Requested digest is too long. BLAKE2X"..inner_func_letter.." finite digest is limited by (2^"..floor(block_size / 2)..")-2 bytes. Hint: you can generate infinite digest.", 2) + end + end + salt = salt or "" + if salt ~= "" then + xor_blake2_salt(salt, inner_func_letter) -- don't xor, only check the size of salt + end + local inner_partial = inner_func(nil, key, salt, nil, XOF_digest_length) + local result + + local function partial(message_part) + if message_part then + if inner_partial then + inner_partial(message_part) + return partial + else + error("Adding more chunks is not allowed after receiving the result", 2) + end + else + if inner_partial then + local half_W, half_W_size = inner_partial() + half_W_size, inner_partial = half_W_size or 8 + + local function get_hash_block(block_no) + -- block_no = 0...(2^32-1) + local size = math_min(block_size, digest_size_in_bytes - block_no * block_size) + if size <= 0 then + return "" + end + for j = 1, half_W_size do + common_W_blake2[j] = half_W[j] + end + for j = half_W_size + 1, 2 * half_W_size do + common_W_blake2[j] = 0 + end + return inner_func(nil, nil, salt, size, XOF_digest_length, floor(block_no)) + end + + local hash = {} + if chunk_by_chunk_output then + local pos, period, cached_block_no, cached_block = 0, block_size * 2^32 + + local function get_next_part_of_digest(arg1, arg2) + if arg1 == "seek" then + -- Usage #1: get_next_part_of_digest("seek", new_pos) + pos = arg2 % period + else + -- Usage #2: hex_string = get_next_part_of_digest(size) + local size, index = arg1 or 1, 0 + while size > 0 do + local block_offset = pos % block_size + local block_no = (pos - block_offset) / block_size + local part_size = math_min(size, block_size - block_offset) + if cached_block_no ~= block_no then + cached_block_no = block_no + cached_block = get_hash_block(block_no) + end + index = index + 1 + hash[index] = sub(cached_block, block_offset * 2 + 1, (block_offset + part_size) * 2) + size = size - part_size + pos = (pos + part_size) % period + end + return table_concat(hash, "", 1, index) + end + end + + result = get_next_part_of_digest + else + for j = 1.0, ceil(digest_size_in_bytes / block_size) do + hash[j] = get_hash_block(j - 1.0) + end + result = table_concat(hash) + end + end + return result + end + end + + if message then + -- Actually perform calculations and return the BLAKE2X digest of a message + return partial(message)() + else + -- Return function for chunk-by-chunk loading + -- User should feed every chunk of input data as single argument to this function and finally get BLAKE2X digest by invoking this function without an argument + return partial + end +end + +local function blake2xs(digest_size_in_bytes, message, key, salt) + -- digest_size_in_bytes: + -- 0..65534 = get finite digest as single Lua string + -- (-1) = get infinite digest in "chunk-by-chunk" output mode + -- (-2)..(-65534) = get finite digest in "chunk-by-chunk" output mode + -- message: binary string to be hashed (or nil for "chunk-by-chunk" input mode) + -- key: (optional) binary string up to 32 bytes, by default empty string + -- salt: (optional) binary string up to 16 bytes, by default empty string + return blake2x(blake2s, "s", common_W_blake2s, 32, digest_size_in_bytes, message, key, salt) +end + +local function blake2xb(digest_size_in_bytes, message, key, salt) + -- digest_size_in_bytes: + -- 0..4294967294 = get finite digest as single Lua string + -- (-1) = get infinite digest in "chunk-by-chunk" output mode + -- (-2)..(-4294967294) = get finite digest in "chunk-by-chunk" output mode + -- message: binary string to be hashed (or nil for "chunk-by-chunk" input mode) + -- key: (optional) binary string up to 64 bytes, by default empty string + -- salt: (optional) binary string up to 32 bytes, by default empty string + return blake2x(blake2b, "b", common_W_blake2b, 64, digest_size_in_bytes, message, key, salt) +end + + +local function blake3(message, key, digest_size_in_bytes, message_flags, K, return_array) + -- message: binary string to be hashed (or nil for "chunk-by-chunk" input mode) + -- key: (optional) binary string up to 32 bytes, by default empty string + -- digest_size_in_bytes: (optional) by default 32 + -- 0,1,2,3,4,... = get finite digest as single Lua string + -- (-1) = get infinite digest in "chunk-by-chunk" output mode + -- -2,-3,-4,... = get finite digest in "chunk-by-chunk" output mode + -- The last three parameters "message_flags", "K" and "return_array" are for internal use only, user must omit them (or pass nil) + key = key or "" + digest_size_in_bytes = digest_size_in_bytes or 32 + message_flags = message_flags or 0 + if key == "" then + K = K or sha2_H_hi + else + local key_length = #key + if key_length > 32 then + error("BLAKE3 key length must not exceed 32 bytes", 2) + end + key = key..string_rep("\0", 32 - key_length) + K = {} + for j = 1, 8 do + local a, b, c, d = byte(key, 4*j-3, 4*j) + K[j] = ((d * 256 + c) * 256 + b) * 256 + a + end + message_flags = message_flags + 16 -- flag:KEYED_HASH + end + local tail, H, chunk_index, blocks_in_chunk, stack_size, stack = "", {}, 0, 0, 0, {} + local final_H_in, final_block_length, chunk_by_chunk_output, result, wide_output = K + local final_compression_flags = 3 -- flags:CHUNK_START,CHUNK_END + + local function feed_blocks(str, offs, size) + -- size >= 0, size is multiple of 64 + while size > 0 do + local part_size_in_blocks, block_flags, H_in = 1, 0, H + if blocks_in_chunk == 0 then + block_flags = 1 -- flag:CHUNK_START + H_in, final_H_in = K, H + final_compression_flags = 2 -- flag:CHUNK_END + elseif blocks_in_chunk == 15 then + block_flags = 2 -- flag:CHUNK_END + final_compression_flags = 3 -- flags:CHUNK_START,CHUNK_END + final_H_in = K + else + part_size_in_blocks = math_min(size / 64, 15 - blocks_in_chunk) + end + local part_size = part_size_in_blocks * 64 + blake3_feed_64(str, offs, part_size, message_flags + block_flags, chunk_index, H_in, H) + offs, size = offs + part_size, size - part_size + blocks_in_chunk = (blocks_in_chunk + part_size_in_blocks) % 16 + if blocks_in_chunk == 0 then + -- completing the currect chunk + chunk_index = chunk_index + 1.0 + local divider = 2.0 + while chunk_index % divider == 0 do + divider = divider * 2.0 + stack_size = stack_size - 8 + for j = 1, 8 do + common_W_blake2s[j] = stack[stack_size + j] + end + for j = 1, 8 do + common_W_blake2s[j + 8] = H[j] + end + blake3_feed_64(nil, 0, 64, message_flags + 4, 0, K, H) -- flag:PARENT + end + for j = 1, 8 do + stack[stack_size + j] = H[j] + end + stack_size = stack_size + 8 + end + end + end + + local function get_hash_block(block_no) + local size = math_min(64, digest_size_in_bytes - block_no * 64) + if block_no < 0 or size <= 0 then + return "" + end + if chunk_by_chunk_output then + for j = 1, 16 do + common_W_blake2s[j] = stack[j + 16] + end + end + blake3_feed_64(nil, 0, 64, final_compression_flags, block_no, final_H_in, stack, wide_output, final_block_length) + if return_array then + return stack + end + local max_reg = ceil(size / 4) + for j = 1, max_reg do + stack[j] = HEX(stack[j]) + end + return sub(gsub(table_concat(stack, "", 1, max_reg), "(..)(..)(..)(..)", "%4%3%2%1"), 1, size * 2) + end + + local function partial(message_part) + if message_part then + if tail then + local offs = 0 + if tail ~= "" and #tail + #message_part > 64 then + offs = 64 - #tail + feed_blocks(tail..sub(message_part, 1, offs), 0, 64) + tail = "" + end + local size = #message_part - offs + local size_tail = size > 0 and (size - 1) % 64 + 1 or 0 + feed_blocks(message_part, offs, size - size_tail) + tail = tail..sub(message_part, #message_part + 1 - size_tail) + return partial + else + error("Adding more chunks is not allowed after receiving the result", 2) + end + else + if tail then + final_block_length = #tail + tail = tail..string_rep("\0", 64 - #tail) + if common_W_blake2s[0] then + for j = 1, 16 do + local a, b, c, d = byte(tail, 4*j-3, 4*j) + common_W_blake2s[j] = OR(SHL(d, 24), SHL(c, 16), SHL(b, 8), a) + end + else + for j = 1, 16 do + local a, b, c, d = byte(tail, 4*j-3, 4*j) + common_W_blake2s[j] = ((d * 256 + c) * 256 + b) * 256 + a + end + end + tail = nil + for stack_size = stack_size - 8, 0, -8 do + blake3_feed_64(nil, 0, 64, message_flags + final_compression_flags, chunk_index, final_H_in, H, nil, final_block_length) + chunk_index, final_block_length, final_H_in, final_compression_flags = 0, 64, K, 4 -- flag:PARENT + for j = 1, 8 do + common_W_blake2s[j] = stack[stack_size + j] + end + for j = 1, 8 do + common_W_blake2s[j + 8] = H[j] + end + end + final_compression_flags = message_flags + final_compression_flags + 8 -- flag:ROOT + if digest_size_in_bytes < 0 then + if digest_size_in_bytes == -1 then -- infinite digest + digest_size_in_bytes = math_huge + else + digest_size_in_bytes = -1.0 * digest_size_in_bytes + end + chunk_by_chunk_output = true + for j = 1, 16 do + stack[j + 16] = common_W_blake2s[j] + end + end + digest_size_in_bytes = math_min(2^53, digest_size_in_bytes) + wide_output = digest_size_in_bytes > 32 + if chunk_by_chunk_output then + local pos, cached_block_no, cached_block = 0.0 + + local function get_next_part_of_digest(arg1, arg2) + if arg1 == "seek" then + -- Usage #1: get_next_part_of_digest("seek", new_pos) + pos = arg2 * 1.0 + else + -- Usage #2: hex_string = get_next_part_of_digest(size) + local size, index = arg1 or 1, 32 + while size > 0 do + local block_offset = pos % 64 + local block_no = (pos - block_offset) / 64 + local part_size = math_min(size, 64 - block_offset) + if cached_block_no ~= block_no then + cached_block_no = block_no + cached_block = get_hash_block(block_no) + end + index = index + 1 + stack[index] = sub(cached_block, block_offset * 2 + 1, (block_offset + part_size) * 2) + size = size - part_size + pos = pos + part_size + end + return table_concat(stack, "", 33, index) + end + end + + result = get_next_part_of_digest + elseif digest_size_in_bytes <= 64 then + result = get_hash_block(0) + else + local last_block_no = ceil(digest_size_in_bytes / 64) - 1 + for block_no = 0.0, last_block_no do + stack[33 + block_no] = get_hash_block(block_no) + end + result = table_concat(stack, "", 33, 33 + last_block_no) + end + end + return result + end + end + + if message then + -- Actually perform calculations and return the BLAKE3 digest of a message + return partial(message)() + else + -- Return function for chunk-by-chunk loading + -- User should feed every chunk of input data as single argument to this function and finally get BLAKE3 digest by invoking this function without an argument + return partial + end +end + +local function blake3_derive_key(key_material, context_string, derived_key_size_in_bytes) + -- key_material: (string) your source of entropy to derive a key from (for example, it can be a master password) + -- set to nil for feeding the key material in "chunk-by-chunk" input mode + -- context_string: (string) unique description of the derived key + -- digest_size_in_bytes: (optional) by default 32 + -- 0,1,2,3,4,... = get finite derived key as single Lua string + -- (-1) = get infinite derived key in "chunk-by-chunk" output mode + -- -2,-3,-4,... = get finite derived key in "chunk-by-chunk" output mode + if type(context_string) ~= "string" then + error("'context_string' parameter must be a Lua string", 2) + end + local K = blake3(context_string, nil, nil, 32, nil, true) -- flag:DERIVE_KEY_CONTEXT + return blake3(key_material, nil, derived_key_size_in_bytes, 64, K) -- flag:DERIVE_KEY_MATERIAL +end + + + +local sha = { + md5 = md5, -- MD5 + sha1 = sha1, -- SHA-1 + -- SHA-2 hash functions: + sha224 = function (message) return sha256ext(224, message) end, -- SHA-224 + sha256 = function (message) return sha256ext(256, message) end, -- SHA-256 + sha512_224 = function (message) return sha512ext(224, message) end, -- SHA-512/224 + sha512_256 = function (message) return sha512ext(256, message) end, -- SHA-512/256 + sha384 = function (message) return sha512ext(384, message) end, -- SHA-384 + sha512 = function (message) return sha512ext(512, message) end, -- SHA-512 + -- SHA-3 hash functions: + sha3_224 = function (message) return keccak((1600 - 2 * 224) / 8, 224 / 8, false, message) end, -- SHA3-224 + sha3_256 = function (message) return keccak((1600 - 2 * 256) / 8, 256 / 8, false, message) end, -- SHA3-256 + sha3_384 = function (message) return keccak((1600 - 2 * 384) / 8, 384 / 8, false, message) end, -- SHA3-384 + sha3_512 = function (message) return keccak((1600 - 2 * 512) / 8, 512 / 8, false, message) end, -- SHA3-512 + shake128 = function (digest_size_in_bytes, message) return keccak((1600 - 2 * 128) / 8, digest_size_in_bytes, true, message) end, -- SHAKE128 + shake256 = function (digest_size_in_bytes, message) return keccak((1600 - 2 * 256) / 8, digest_size_in_bytes, true, message) end, -- SHAKE256 + -- HMAC: + hmac = hmac, -- HMAC(hash_func, key, message) is applicable to any hash function from this module except SHAKE* and BLAKE* + -- misc utilities: + hex_to_bin = hex_to_bin, -- converts hexadecimal representation to binary string + bin_to_hex = bin_to_hex, -- converts binary string to hexadecimal representation + base64_to_bin = base64_to_bin, -- converts base64 representation to binary string + bin_to_base64 = bin_to_base64, -- converts binary string to base64 representation + -- old style names for backward compatibility: + hex2bin = hex_to_bin, + bin2hex = bin_to_hex, + base642bin = base64_to_bin, + bin2base64 = bin_to_base64, + -- BLAKE2 hash functions: + blake2b = blake2b, -- BLAKE2b (message, key, salt, digest_size_in_bytes) + blake2s = blake2s, -- BLAKE2s (message, key, salt, digest_size_in_bytes) + blake2bp = blake2bp, -- BLAKE2bp(message, key, salt, digest_size_in_bytes) + blake2sp = blake2sp, -- BLAKE2sp(message, key, salt, digest_size_in_bytes) + blake2xb = blake2xb, -- BLAKE2Xb(digest_size_in_bytes, message, key, salt) + blake2xs = blake2xs, -- BLAKE2Xs(digest_size_in_bytes, message, key, salt) + -- BLAKE2 aliases: + blake2 = blake2b, + blake2b_160 = function (message, key, salt) return blake2b(message, key, salt, 20) end, -- BLAKE2b-160 + blake2b_256 = function (message, key, salt) return blake2b(message, key, salt, 32) end, -- BLAKE2b-256 + blake2b_384 = function (message, key, salt) return blake2b(message, key, salt, 48) end, -- BLAKE2b-384 + blake2b_512 = blake2b, -- 64 -- BLAKE2b-512 + blake2s_128 = function (message, key, salt) return blake2s(message, key, salt, 16) end, -- BLAKE2s-128 + blake2s_160 = function (message, key, salt) return blake2s(message, key, salt, 20) end, -- BLAKE2s-160 + blake2s_224 = function (message, key, salt) return blake2s(message, key, salt, 28) end, -- BLAKE2s-224 + blake2s_256 = blake2s, -- 32 -- BLAKE2s-256 + -- BLAKE3 hash function + blake3 = blake3, -- BLAKE3 (message, key, digest_size_in_bytes) + blake3_derive_key = blake3_derive_key, -- BLAKE3_KDF(key_material, context_string, derived_key_size_in_bytes) +} + + +block_size_for_HMAC = { + [sha.md5] = 64, + [sha.sha1] = 64, + [sha.sha224] = 64, + [sha.sha256] = 64, + [sha.sha512_224] = 128, + [sha.sha512_256] = 128, + [sha.sha384] = 128, + [sha.sha512] = 128, + [sha.sha3_224] = 144, -- (1600 - 2 * 224) / 8 + [sha.sha3_256] = 136, -- (1600 - 2 * 256) / 8 + [sha.sha3_384] = 104, -- (1600 - 2 * 384) / 8 + [sha.sha3_512] = 72, -- (1600 - 2 * 512) / 8 +} + + +return sha diff --git a/Zframework/stringExtend.lua b/Zframework/stringExtend.lua new file mode 100644 index 000000000..141fc897d --- /dev/null +++ b/Zframework/stringExtend.lua @@ -0,0 +1,428 @@ +local data=love.data +local STRING={} +local assert,tostring,tonumber=assert,tostring,tonumber +local floorint,format=math.floor,string.format +local find,sub,gsub=string.find,string.sub,string.gsub +local rep,upper=string.rep,string.upper +local char,byte=string.char,string.byte + +-- "Replace dollars", replace all $n with ... +function STRING.repD(str,...) + local l={...} + for i=#l,1,-1 do + str=gsub(str,'$'..i,l[i]) + end + return str +end + +-- "Scan arg", scan if str has the arg (format of str is like "-json -q", arg is like "-q") +function STRING.sArg(str,switch) + if find(str.." ",switch.." ") then + return true + end +end + +do-- function STRING.shiftChar(c) + local shiftMap={ + ['1']='!',['2']='@',['3']='#',['4']='$',['5']='%', + ['6']='^',['7']='&',['8']='*',['9']='(',['0']=')', + ['`']='~',['-']='_',['=']='+', + ['[']='{',[']']='}',['\\']='|', + [';']=':',['\'']='"', + [',']='<',['.']='>',['/']='?', + } + function STRING.shiftChar(c) + return shiftMap[c] or upper(c) + end +end + +-- [LOW PERFORMANCE!] +local upperData,lowerData,diaData +function STRING.upperUTF8(str) + for _,pair in next,upperData do + str=str:gsub(pair[1],pair[2]) + end + return str +end +function STRING.lowerUTF8(str) + for _,pair in next,lowerData do + str=str:gsub(pair[1],pair[2]) + end + return str +end +function STRING.remDiacritics(str) + for _,pair in next,diaData do + str=str:gsub(pair[1],pair[2]) + end + return str +end + +function STRING.trim(s) + if not s:find("%S") then return "" end + s=s:sub((s:find("%S"))):reverse() + return s:sub((s:find("%S"))):reverse() +end + +function STRING.split(s,sep,regex) + local L={} + local p1,p2=1-- start,target + if regex then + while p1<=#s do + p2=find(s,sep,p1) or #s+1 + L[#L+1]=sub(s,p1,p2-1) + p1=p2+#sep + end + else + while p1<=#s do + p2=find(s,sep,p1,true) or #s+1 + L[#L+1]=sub(s,p1,p2-1) + p1=p2+#sep + end + end + return L +end + +function STRING.simpEmailCheck(e) + e=STRING.split(e,"@") + if #e~=2 then return false end + if e[1]:sub(-1)=="." or e[2]:sub(-1)=="." then return false end + local e1,e2=STRING.split(e[1],"."),STRING.split(e[2],".") + if #e1*#e2==0 then return false end + for _,v in next,e1 do if #v==0 then return false end end + for _,v in next,e2 do if #v==0 then return false end end + return true +end + +local MINUTE=60 +local HOUR=3600 +local DAY=86400 +local YEAR=31536000 -- 365 days +local function convertSecondsToUnits(t) -- convert seconds to {seconds, minutes, hours, days, years} + local years=floorint(t/YEAR) + local remainder=t%YEAR + + local days=floorint(remainder/DAY) + remainder=remainder%DAY + + local hours=floorint(remainder/HOUR) + remainder=remainder%HOUR + + local minutes=floorint(remainder/MINUTE) + local seconds=remainder%MINUTE + return seconds,minutes,hours,days,years +end + +-- MM:SS +function STRING.time_simp(t) + return format("%02d:%02d",floorint(t/MINUTE),floorint(t%MINUTE)) +end + +local timeLetters={' y',' d',' h',' m',' s',' ms'} +-- Display 2 largest units of time. +function STRING.time_short(t) + -- Early returns to prevent nil values + if t<0 then return '-'..STRING.time_short(-t) end -- negative time + if t<1 then return math.floor(t*1000)..timeLetters[6] end -- 123 ms + if t0 then + return timeUnits[i]..timeLetters[i]..' '..timeUnits[i+1]..timeLetters[i+1] + end + end +end + +function STRING.time(t) + local s,m,h,d,y=convertSecondsToUnits(t) + if t=0 and n<2^31,"Out of range ("..n..")") + if n<2^7 then return char(n) + elseif n<2^11 then return char(192+floorint(n/2^06),128+n%2^6) + elseif n<2^16 then return char(224+floorint(n/2^12),128+floorint(n/2^06)%2^6,128+n%2^6) + elseif n<2^21 then return char(240+floorint(n/2^18),128+floorint(n/2^12)%2^6,128+floorint(n/2^06)%2^6,128+n%2^6) + elseif n<2^26 then return char(248+floorint(n/2^24),128+floorint(n/2^18)%2^6,128+floorint(n/2^12)%2^6,128+floorint(n/2^06)%2^6,128+n%2^6) + elseif n<2^31 then return char(252+floorint(n/2^30),128+floorint(n/2^24)%2^6,128+floorint(n/2^18)%2^6,128+floorint(n/2^12)%2^6,128+floorint(n/2^06)%2^6,128+n%2^6) + end +end + +do-- functions to shorted big numbers + local lg=math.log10 + local units={"","K","M","B","T","Qa","Qt","Sx","Sp","Oc","No"} + local preUnits={"","U","D","T","Qa","Qt","Sx","Sp","O","N"} + local secUnits={"Dc","Vg","Tg","Qd","Qi","Se","St","Og","Nn","Ce"}-- Ce is next-level unit, but DcCe is not used so used here + for _,preU in next,preUnits do for _,secU in next,secUnits do table.insert(units,preU..secU) end end + function STRING.bigInt(t) + if t<1000 then + return tostring(t) + elseif t~=1/0 then + local e=floorint(lg(t)/3) + return(t/10^(e*3))..units[e+1] + else + return "INF" + end + end + + local MIN_SI=-30 -- current lowest order of magnitude for SI units (quecto-; 10^-30) + local MAX_SI=30 -- current highest order of magnitude for SI units (quetta-; 10^30) + local SI_SHORT={ + [-30]='q',[-27]='r',[-24]='y',[-21]='z',[-18]='a',[-15]='f',[-12]='p', + [-9]='n',[-6]='μ',[-3]='m',[0]='',[3]='k',[6]='M',[9]='G', + [12]='T',[15]='P',[18]='E',[21]='Z',[24]='Y',[27]='R',[30]='Q' + } + local SI_LONG={ + [-30]='quecto',[-27]='ronto',[-24]='yocto',[-21]='zepto',[-18]='atto',[-15]='femto',[-12]='pico', + [-9]='nano',[-6]='micro',[-3]='milli',[0]='',[3]='kilo',[6]='mega',[9]='giga', + [12]='tera',[15]='peta',[18]='exa',[21]='zetta',[24]='yotta',[27]='ronna',[30]='quetta' + } + --[[ + Converts a number into SI notation with letter prefixes. + NOTE: Only power-of-thousand prefixes; no deci-/centi-. + Arguments: + - num: The number to be converted to SI notation. + - unit: [optional] The unit to be concatenated at the end. + Example: STRING.SI(10^-9,"m") --> "1 nm" + ]] + function STRING.SI(num, unit) + unit=unit or '' + local order=MATH.clamp(3*math.floor(math.log10(num)/3),MIN_SI,MAX_SI) + local prefix=SI_SHORT[order] + local scaledNum=num/10^order + local formattedNum=string.format('%.3f', scaledNum):gsub('%.?0+$','') + return formattedNum.." "..prefix..unit + end + + --[[ + Converts a number into SI notation with word prefixes. + NOTE: Only power-of-thousand prefixes; no deci-/centi-. + Arguments: + - num: The number to be converted to SI notation. + - unit: [optional] The unit to be concatenated at the end. + Example: STRING.SI(10^9,"hertz") --> "1 megahertz" + ]] + function STRING.SILong(num, unit) + unit=unit or '' + local order=MATH.clamp(3*math.floor(math.log10(num)/3),MIN_SI,MAX_SI) + local prefix=SI_LONG[order] + local scaledNum=num/10^order + local formattedNum=string.format('%.3f', scaledNum):gsub('%.?0+$','') + return formattedNum.." "..prefix..unit + end +end + +do-- function STRING.toBin, STRING.toOct, STRING.toHex(n,len) + function STRING.toBin(n,len) + local s="" + while n>0 do + s=(n%2)..s + n=floorint(n/2) + end + if len then + return rep("0",len-#s)..s + else + return s + end + end + function STRING.toOct(n,len) + local s="" + while n>0 do + s=(n%8)..s + n=floorint(n/8) + end + if len then + return rep("0",len-#s)..s + else + return s + end + end + local b16={[0]='0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'} + function STRING.toHex(n,len) + local s="" + while n>0 do + s=b16[n%16]..s + n=floorint(n/16) + end + if len then + return rep("0",len-#s)..s + else + return s + end + end +end + +function STRING.hexColor(str)--[LOW PERFORMENCE] + assert(type(str)=='string') + if str:sub(1,1)=="#" then str=str:sub(2) end + assert(#str<=8) + local r=(tonumber(str:sub(1,2),16) or 0)/255 + local g=(tonumber(str:sub(3,4),16) or 0)/255 + local b=(tonumber(str:sub(5,6),16) or 0)/255 + local a=(tonumber(str:sub(7,8),16) or 255)/255 + return r,g,b,a +end + +do-- function STRING.urlEncode(s) + local rshift=bit.rshift + local b16={[0]='0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'} + function STRING.urlEncode(s) + local out="" + for i=1,#s do + if s:sub(i,i):match("[a-zA-Z0-9]") then + out=out..s:sub(i,i) + else + local b=s:byte(i) + out=out.."%"..b16[rshift(b,4)]..b16[b%16] + end + end + return out + end +end + +function STRING.vcsEncrypt(text,key) + local keyLen=#key + local result="" + local buffer="" + for i=0,#text-1 do + buffer=buffer..char((byte(text,i+1)-32+byte(key,i%keyLen+1))%95+32) + if #buffer==26 then + result=result..buffer + buffer="" + end + end + return result..buffer +end +function STRING.vcsDecrypt(text,key) + local keyLen=#key + local result="" + local buffer="" + for i=0,#text-1 do + buffer=buffer..char((byte(text,i+1)-32-byte(key,i%keyLen+1))%95+32) + if #buffer==26 then + result=result..buffer + buffer="" + end + end + return result..buffer +end +function STRING.digezt(text)-- Not powerful hash, just protect the original text + local out={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0} + local seed=26 + for i=1,#text do + local c=byte(text,i) + seed=(seed+c)%26 + c=c+seed + local pos=c*i%16 + local step=(c+i)%4+1 + local times=2+(c%6) + for _=1,times do + out[pos+1]=(out[pos+1]+c)%256 + pos=(pos+step)%16 + end + end + local result="" + for i=1,16 do result=result..char(out[i]) end + return result +end + +function STRING.readLine(str) + local p=str:find("\n") + if p then + return str:sub(1,p-1),str:sub(p+1) + else + return str,"" + end +end +function STRING.readChars(str,n) + return sub(str,1,n),sub(str,n+1) +end + +function STRING.packBin(s) + return data.encode('string','base64',data.compress('string','zlib',s)) +end +function STRING.unpackBin(str) + local res + res,str=pcall(data.decode,'string','base64',str) + if not res then return end + res,str=pcall(data.decompress,'string','zlib',str) + if res then return str end +end +function STRING.packText(s) + return data.encode('string','base64',data.compress('string','gzip',s)) +end +function STRING.unpackText(str) + local res + res,str=pcall(data.decode,'string','base64',str) + if not res then return end + res,str=pcall(data.decompress,'string','gzip',str) + if res then return str end +end +function STRING.packTable(t) + return STRING.packText(JSON.encode(t)) +end +function STRING.unpackTable(t) + return JSON.decode(STRING.unpackText(t)) +end + +do + local function parseFile(fname) + local d + if love and love.filesystem and type(love.filesystem.read)=='function' then + d=love.filesystem.read(fname) + else + local f=io.open(fname,'r') + if f then + d=f:read('a') + f:close() + end + end + + if not d then + print("ERROR: Failed to read the data from "..fname) + return {} + end + d=STRING.split(gsub(d,'\n',','),',') + for i=1,#d do + d[i]=STRING.split(d[i],'=') + end + return d + end + upperData=parseFile('Zframework/upcaser.txt') + lowerData=parseFile('Zframework/lowcaser.txt') + diaData=parseFile('Zframework/diacritics.txt') +end + +return STRING diff --git a/Zframework/sysFX.lua b/Zframework/sysFX.lua new file mode 100644 index 000000000..4b6e6c86f --- /dev/null +++ b/Zframework/sysFX.lua @@ -0,0 +1,205 @@ +local gc=love.graphics +local gc_setColor,gc_setLineWidth=gc.setColor,gc.setLineWidth +local gc_draw,gc_line=gc.draw,gc.line +local gc_rectangle,gc_circle=gc.rectangle,gc.circle + +local max,min=math.max,math.min +local rnd=math.random +local ins,rem=table.insert,table.remove + +local fx={} + +local function _normUpdate(S,dt) + S.t=S.t+dt*S.rate + return S.t>1 +end + +local FXupdate={} +function FXupdate.badge(S,dt) + S.t=S.t+dt + if S.t<.2 then + S.x,S.y=S.x1-14,S.y1-14 + elseif S.t<.8 then + local t=((S.t-.2)*1.6667) + t=(3-2*t)*t*t + S.x,S.y=S.x1*(1-t)+S.x2*t-14,S.y1*(1-t)+S.y2*t-14 + else + S.x,S.y=S.x2-14,S.y2-14 + end + return S.t>=1 +end +FXupdate.attack=_normUpdate +FXupdate.tap=_normUpdate +FXupdate.ripple=_normUpdate +FXupdate.rectRipple=_normUpdate +FXupdate.shade=_normUpdate +function FXupdate.cell(S,dt) + if S.vx then + S.x=S.x+S.vx*S.rate + S.y=S.y+S.vy*S.rate + if S.ax then + S.vx=S.vx+S.ax*S.rate + S.vy=S.vy+S.ay*S.rate + end + end + S.t=S.t+dt*S.rate + return S.t>1 +end +FXupdate.line=_normUpdate + +local FXdraw={} +function FXdraw.badge(S) + gc_setColor(1,1,1,S.t<.2 and S.t*.6 or S.t<.8 and 1 or (1-S.t)*.6) + gc_draw(IMG.badgeIcon,S.x,S.y) +end +function FXdraw.attack(S) + gc_setColor(S.r*2,S.g*2,S.b*2,S.a*min(4-S.t*4,1)) + + gc_setLineWidth(S.wid) + local t1,t2=max(5*S.t-4,0),min(S.t*4,1) + gc_line( + S.x1*(1-t1)+S.x2*t1, + S.y1*(1-t1)+S.y2*t1, + S.x1*(1-t2)+S.x2*t2, + S.y1*(1-t2)+S.y2*t2 + ) + + gc_setLineWidth(S.wid*.6) + t1,t2=max(4*S.t-3,0),min(S.t*5,1) + gc_line( + S.x1*(1-t1)+S.x2*t1, + S.y1*(1-t1)+S.y2*t1, + S.x1*(1-t2)+S.x2*t2, + S.y1*(1-t2)+S.y2*t2 + ) +end +function FXdraw.tap(S) + local t=S.t + gc_setColor(1,1,1,(1-t)*.4) + gc_circle('fill',S.x,S.y,30*(1-t)^.5) +end +function FXdraw.ripple(S) + local t=S.t + gc_setLineWidth(2) + gc_setColor(1,1,1,1-t) + gc_circle('line',S.x,S.y,t*(2-t)*S.r) +end +function FXdraw.rectRipple(S) + gc_setLineWidth(6) + gc_setColor(1,1,1,1-S.t) + local r=(10*S.t)^1.2 + gc_rectangle('line',S.x-r,S.y-r,S.w+2*r,S.h+2*r) +end +function FXdraw.shade(S) + gc_setColor(S.r,S.g,S.b,1-S.t) + gc_rectangle('fill',S.x,S.y,S.w,S.h,2) +end +function FXdraw.cell(S) + gc_setColor(1,1,1,1-S.t) + gc_draw(S.image,S.x,S.y,nil,S.size,nil,S.cx,S.cy) +end +function FXdraw.line(S) + gc_setColor(1,1,1,S.a*(1-S.t)) + gc_line(S.x1,S.y1,S.x2,S.y2) +end + +local SYSFX={} +function SYSFX.update(dt) + for i=#fx,1,-1 do + if fx[i]:update(dt) then + rem(fx,i) + end + end +end +function SYSFX.draw() + for i=1,#fx do + fx[i]:draw() + end +end + +function SYSFX.newBadge(x1,y1,x2,y2) + ins(fx,{ + update=FXupdate.badge, + draw=FXdraw.badge, + t=0, + x=x1,y=y1, + x1=x1,y1=y1, + x2=x2,y2=y2, + }) +end +function SYSFX.newAttack(rate,x1,y1,x2,y2,wid,r,g,b,a) + ins(fx,{ + update=FXupdate.attack, + draw=FXdraw.attack, + t=0, + rate=rate, + x1=x1,y1=y1,-- Start pos + x2=x2,y2=y2,-- End pos + wid=wid,-- Line width + r=r,g=g,b=b,a=a, + }) +end +function SYSFX.newTap(rate,x,y) + local T= + { + update=FXupdate.tap, + draw=FXdraw.tap, + t=0, + rate=rate, + x=x,y=y, + } + ins(fx,T) +end +function SYSFX.newRipple(rate,x,y,r) + ins(fx,{ + update=FXupdate.ripple, + draw=FXdraw.ripple, + t=0, + rate=rate, + x=x,y=y,r=r, + }) +end +function SYSFX.newRectRipple(rate,x,y,w,h) + ins(fx,{ + update=FXupdate.rectRipple, + draw=FXdraw.rectRipple, + t=0, + rate=rate, + x=x,y=y,w=w,h=h, + }) +end +function SYSFX.newShade(rate,x,y,w,h,r,g,b) + ins(fx,{ + update=FXupdate.shade, + draw=FXdraw.shade, + t=0, + rate=rate, + x=x,y=y,w=w,h=h, + r=r or 1,g=g or 1,b=b or 1, + }) +end +function SYSFX.newCell(rate,image,size,x,y,vx,vy,ax,ay) + ins(fx,{ + update=FXupdate.cell, + draw=FXdraw.cell, + t=0, + rate=rate*(.9+rnd()*.2), + image=image,size=size, + cx=image:getWidth()*.5,cy=image:getHeight()*.5, + x=x,y=y, + vx=vx,vy=vy, + ax=ax,ay=ay, + }) +end +function SYSFX.newLine(rate,x1,y1,x2,y2,r,g,b,a) + ins(fx,{ + update=FXupdate.line, + draw=FXdraw.line, + t=0, + rate=rate, + x1=x1 or 0,y1=y1 or 0, + x2=x2 or x1 or 1280,y2=y2 or y1 or 720, + r=r or 1,g=g or 1,b=b or 1,a=a or 1, + }) +end +return SYSFX diff --git a/Zframework/tableExtend.lua b/Zframework/tableExtend.lua new file mode 100644 index 000000000..9417157c6 --- /dev/null +++ b/Zframework/tableExtend.lua @@ -0,0 +1,463 @@ +local rnd=math.random +local min,max=math.min,math.max +local find=string.find +local ins,rem=table.insert,table.remove +local next,type=next,type +local TABLE={} + +-----------------------[Making Tables]------------------------ +-- Get a new filled table +function TABLE.new(val,count) + local L={} + for i=1,count do + L[i]=val + end + return L +end + +-- Get a copy of [1~#] elements +function TABLE.shift(org,depth) + if not depth then depth=1e99 end + local L={} + for i=1,#org do + if type(org[i])=='table' and depth>0 then + L[i]=TABLE.shift(org[i],depth-1) + else + L[i]=org[i] + end + end + return L +end + +-- Get a full copy of a table, depth=how many layers will be recreate, default to inf +function TABLE.copy(org,depth) + if not depth then depth=1e99 end + local L={} + for k,v in next,org do + if type(v)=='table' and depth>0 then + L[k]=TABLE.copy(v,depth-1) + else + L[k]=v + end + end + return L +end + +-- Connect [1~#] elements of new to the end of org +function TABLE.connect(org,new) + local l0=#org + for i=1,#new do + org[l0+i]=new[i] + end + return org +end + +-- Get a table of two lists connected +function TABLE.combine(L1,L2) + local l={} + local l0=#L1 + for i=1,l0 do l[i]=L1[i] end + for i=1,#L2 do l[l0+i]=L2[i] end + return l +end + +----------------------[Modifying Tables]---------------------- + +-- For all things in new, push to old +function TABLE.cover(new,old) + for k,v in next,new do + old[k]=v + end +end + +-- For all things in new, push to old +function TABLE.coverR(new,old) + for k,v in next,new do + if type(v)=='table' and type(old[k])=='table' then + TABLE.coverR(v,old[k]) + else + old[k]=v + end + end +end + +-- For all things in org, delete them if it's in sub +function TABLE.subtract(org,sub) + for _,v in next,sub do + while true do + local p=TABLE.search(org,v) + if p then + rem(org,p) + else + break + end + end + end +end + +-- For all things in new if same type in old, push to old +function TABLE.update(new,old) + for k,v in next,new do + if type(v)==type(old[k]) then + if type(v)=='table' then + TABLE.update(v,old[k]) + else + old[k]=v + end + end + end +end + +-- For all things in new if no val in old, push to old +function TABLE.complete(new,old) + for k,v in next,new do + if type(v)=='table' then + if old[k]==nil then old[k]={} end + TABLE.complete(v,old[k]) + elseif old[k]==nil then + old[k]=v + end + end +end + +-------------------[Removing Table Values]-------------------- + +-- Pop & return random [1~#] of table +function TABLE.popRandom(t) + local l=#t + if l>0 then + local r=rnd(l) + r,t[r]=t[r],t[l] + t[l]=nil + return r + end +end + +-- Remove [1~#] of table +function TABLE.cut(G) + for i=1,#G do + G[i]=nil + end +end + +-- Clear table +function TABLE.clear(G) + for k in next,G do + G[k]=nil + end +end + +--------------------[Handling duplicates]--------------------- + +-- Remove duplicated value of [1~#] +function TABLE.trimDuplicate(org) + local cache={} + for i=1,#org,-1 do + if cache[org[i]] then + rem(org,i) + else + cache[org[i]]=true + end + end +end + +-- Discard duplicated value +function TABLE.remDuplicate(org) + local cache={} + for k,v in next,org do + if cache[v] then + org[k]=nil + else + cache[v]=true + end + end +end + +--[[ + Run length encoder. Input must be a list containing non-nil value(s). + Example: + - Input: {1, 1, 2, 2, 2, 1} + - Output: {{1, 2}, {2, 3}, {1, 1}} + - This means: "Two 1's in a row", "Three 2's in a row", "One 1 in a row" +]] +function TABLE.RLE(org) + local output={} + local cur=nil + local count=0 + + for i=1,#org do + local item=org[i] + + if item==cur then + count=count+1 + else + if cur then + ins(output,{cur,count}) + end + cur=item + count=1 + end + end + + if cur then + ins(output,{cur,count}) + end + + return output +end + +----------------------[Reversing Tables]---------------------- + +-- Reverse [1~#] +function TABLE.reverse(org) + local l=#org + for i=1,math.floor(l/2) do + org[i],org[l+1-i]=org[l+1-i],org[i] + end +end + +----------------------[Table Comparison]---------------------- + +-- Check if tow list have same elements +function TABLE.compare(a,b) + if #a~=#b then return false end + if a==b then return true end + for i=1,#a do + if a[i]~=b[i] then return false end + end + return true +end + +-- Check if tow table have same elements +function TABLE.equal(a,b) + if #a~=#b then return false end + if a==b then return true end + for k,v in next,a do + if b[k]~=v then return false end + end + return true +end + +----------------------[Table Operations]---------------------- + +-- Find value in [1~#], like string.find +function TABLE.find(t,val,start) + for i=start or 1,#t do if t[i]==val then return i end end +end + +-- Get subset of table, like string.sub +function TABLE.sub(t,i,j) + local subTable={} + for k=max(i,1),(j and min(j,#t) or #t) do + subTable[k-i+1]=t[k] + end + return subTable +end + +-- Replace value in [1~#], like string.gsub +function TABLE.gsub(t,v_old,v_new,count,start) + if not start then start=1 end + if not count then count=1e99 end + while t[start] and count>0 do + if t[start]==v_old then + t[start]=v_new + count=count-1 + end + end +end + +-- Return next value of [1~#] (by value) +function TABLE.next(t,val) + for i=1,#t do if t[i]==val then return t[i%#t+1] end end +end + +-- Find value in whole table +function TABLE.search(t,val) + for k,v in next,t do if v==val then return k end end +end + +-- Replace all value in t +function TABLE.replace(t,v_old,v_new) + for k,v in next,t do + if v==v_old then + t[k]=v_new + end + end +end + +-- Re-index string value of a table +function TABLE.reIndex(org) + for k,v in next,org do + if type(v)=='string' then + org[k]=org[v] + end + end +end + +-- Return table where keys and values are swapped (useful for hashmap) +function TABLE.kvSwap(t) + local output={} + for k,v in next,t do output[v]=k end + return output +end + +--[[ + Extracts a value from each sub-table in table. + Example input: ({{name='A'},{name='B'},{name='C'}}, 'name') + Output: {'A','B'} +]] +function TABLE.extract(t,keyName) + local output={} + for k,v in next,t do output[k]=v[keyName] end + return output +end + +-- Get element count of table +function TABLE.getSize(t) + local size=0 + for _ in next,t do size=size+1 end + return size +end + +-----------------------[Table Rotation]----------------------- + +-- Copy a rotated matrix table +function TABLE.rotate(cb,dir) + local icb={} + if dir=='R' then-- Rotate CW + for y=1,#cb[1] do + icb[y]={} + for x=1,#cb do + icb[y][x]=cb[x][#cb[1]-y+1] + end + end + elseif dir=='L' then-- Rotate CCW + for y=1,#cb[1] do + icb[y]={} + for x=1,#cb do + icb[y][x]=cb[#cb-x+1][y] + end + end + elseif dir=='F' then-- Rotate 180 degree + for y=1,#cb do + icb[y]={} + for x=1,#cb[1] do + icb[y][x]=cb[#cb-y+1][#cb[1]-x+1] + end + end + end + return icb +end + +----------------------[Table Functions]----------------------- + +-- Return a function that return a value of table +function TABLE.func_getVal(t,k) + return function() return t[k] end +end + +-- Return a function that reverse a value of table +function TABLE.func_revVal(t,k) + return function() t[k]=not t[k] end +end + +-- Return a function that set a value of table +function TABLE.func_setVal(t,k) + return function(v) t[k]=v end +end + +-------------------------[Table Dump]------------------------- + +-- Dump a simple lua table (no whitespaces) +do-- function TABLE.dumpDeflate(L,t) + local function dump(L) + if type(L)~='table' then return end + local s='{' + local count=1 + for k,v in next,L do + local T=type(k) + if T=='number' then + if k==count then + k='' + count=count+1 + else + k='['..k..']=' + end + elseif T=='string' then + if find(k,'[^0-9a-zA-Z_]') then + k='[\''..k..'\']=' + else + k=k..'=' + end + elseif T=='boolean' then k='['..k..']=' + else error("Error key type!") + end + T=type(v) + if T=='number' then v=tostring(v) + elseif T=='string' then v='\''..v..'\'' + elseif T=='table' then v=dump(v) + elseif T=='boolean' then v=tostring(v) + else v='*'..tostring(v) + end + s=s..k..v..',' + end + return s..'}' + end + TABLE.dumpDeflate=dump +end + +-- Dump a simple lua table +do-- function TABLE.dump(L,t) + local tabs=setmetatable({ + [0]='', + '\t', + },{__index=function(self,k) + if k>=626 then error("Too many tabs!") end + for i=#self+1,k do + self[i]=self[i-1]..'\t' + end + return self[k] + end}) + local function dump(L,t) + local s='{\n' + if not t then + s='return {\n' + t=1 + if type(L)~='table' then + return + end + end + local count=1 + for k,v in next,L do + local T=type(k) + if T=='number' then + if k==count then + k='' + count=count+1 + else + k='['..k..']=' + end + elseif T=='string' then + if find(k,'[^0-9a-zA-Z_]') then + k='[\''..k..'\']=' + else + k=k..'=' + end + elseif T=='boolean' then k='['..k..']=' + else k='[\'*'..tostring(k)..'\']=' + end + T=type(v) + if T=='number' then v=tostring(v) + elseif T=='string' then v='\''..v..'\'' + elseif T=='table' then v=dump(v,t+1) + elseif T=='boolean' then v=tostring(v) + else v='*'..tostring(v) + end + s=s..tabs[t]..k..v..',\n' + end + return s..tabs[t-1]..'}' + end + TABLE.dump=dump +end + +return TABLE diff --git a/Zframework/task.lua b/Zframework/task.lua new file mode 100644 index 000000000..72f9f0a91 --- /dev/null +++ b/Zframework/task.lua @@ -0,0 +1,83 @@ +local rem=table.remove +local assert,resume,status=assert,coroutine.resume,coroutine.status +local rawset=rawset +local timer=love.timer.getTime + +local TASK={} + +-- Locks +local locks=setmetatable({},{ + __index=function(self,k) rawset(self,k,-1e99)return -1e99 end, + __newindex=function(self,k) rawset(self,k,-1e99) end, +}) +function TASK.lock(name,T) + if timer()>=locks[name] then + locks[name]=timer()+(T or 1e99) + return true + else + return false + end +end +function TASK.unlock(name) + locks[name]=-1e99 +end +function TASK.getLock(name) + local v=locks[name]-timer() + return v>0 and v +end +function TASK.clearLock() + for k in next,locks do + locks[k]=nil + end +end + +local tasks={} + +function TASK.getCount() + return #tasks +end +local trigFrame=0 +function TASK.update(dt) + trigFrame=trigFrame+dt*60 + for _=1,trigFrame do + for i=#tasks,1,-1 do + local T=tasks[i] + if status(T.thread)=='dead' then + rem(tasks,i) + else + assert(resume(T.thread,dt/trigFrame)) + end + end + end + trigFrame=1 +end +function TASK.new(code,...) + local thread=coroutine.create(code) + assert(resume(thread,...)) + if status(thread)~='dead' then + tasks[#tasks+1]={ + thread=thread, + code=code, + args={...}, + } + end +end +function TASK.removeTask_code(code) + for i=#tasks,1,-1 do + if tasks[i].code==code then + rem(tasks,i) + end + end +end +function TASK.removeTask_iterate(func,...) + for i=#tasks,1,-1 do + if func(tasks[i],...) then + rem(tasks,i) + end + end +end +function TASK.clear() + TABLE.cut(tasks) +end + +return TASK diff --git a/Zframework/test.lua b/Zframework/test.lua new file mode 100644 index 000000000..e9faefece --- /dev/null +++ b/Zframework/test.lua @@ -0,0 +1,18 @@ +local yield=coroutine.yield +local TEST={} + +-- Wait for the scene swapping animation to finish +function TEST.yieldUntilNextScene() + while SCN.swapping do yield() end +end + +function TEST.yieldN(frames) + for _=1,frames do yield() end +end + +function TEST.yieldT(timeout) + local t=0 + repeat t=t+yield() until t>=timeout +end + +return TEST diff --git a/Zframework/text.lua b/Zframework/text.lua new file mode 100644 index 000000000..851aadf08 --- /dev/null +++ b/Zframework/text.lua @@ -0,0 +1,152 @@ +local getColor,setColor=GC.getColor,GC.setColor + +local floor,rnd=math.floor,math.random +local ins,rem=table.insert,table.remove +local draw=GC.draw + +local texts={} + +local textFX={} +function textFX.appear(t) + draw( + t.text,t.x,t.y, + nil, + nil,nil, + t.text:getWidth()*.5,t.text:getHeight()*.5 + ) +end +function textFX.sudden(t) + setColor(1,1,1,1-t.c) + draw( + t.text,t.x,t.y, + nil, + nil,nil, + t.text:getWidth()*.5,t.text:getHeight()*.5 + ) +end +function textFX.fly(t) + draw( + t.text,t.x+(t.c-.5)^3*300,t.y, + nil, + nil,nil, + t.text:getWidth()*.5,t.text:getHeight()*.5 + ) +end +function textFX.stretch(t) + draw( + t.text,t.x,t.y, + nil, + t.c<.3 and (.3-t.c)*1.6+1 or 1,1, + t.text:getWidth()*.5,t.text:getHeight()*.5 + ) +end +function textFX.drive(t) + draw( + t.text,t.x,t.y, + nil, + nil,nil, + t.text:getWidth()*.5,t.text:getHeight()*.5, + t.c<.3 and (.3-t.c)*2 or 0,0 + ) +end +function textFX.spin(t) + draw( + t.text,t.x,t.y, + t.c<.3 and (.3-t.c)^2*4 or t.c<.8 and 0 or (t.c-.8)^2*-4, + nil,nil, + t.text:getWidth()*.5,t.text:getHeight()*.5 + ) +end +function textFX.flicker(t) + local _,_,_,T=getColor() + setColor(1,1,1,T*(rnd()+.5)) + draw( + t.text,t.x,t.y, + nil, + nil,nil, + t.text:getWidth()*.5,t.text:getHeight()*.5 + ) +end +function textFX.zoomout(t) + draw( + t.text,t.x,t.y, + nil, + t.c^.5*.1+1,nil, + t.text:getWidth()*.5,t.text:getHeight()*.5 + ) +end +function textFX.beat(t) + local k=t.c<.3 and 1.3-t.c^2/.3 or 1 + draw( + t.text,t.x,t.y, + nil, + k,k, + t.text:getWidth()*.5,t.text:getHeight()*.5 + ) +end +function textFX.score(t) + local _,_,_,T=getColor() + setColor(1,1,1,T*.5) + draw( + t.text,t.x,t.y-0-t.c^.2*50, + nil, + nil,nil, + t.text:getWidth()*.5,t.text:getHeight()*.5 + ) +end + +local TEXT={} +function TEXT.clear() + texts={} +end +function TEXT.show(text,x,y,font,style,spd,stop) + ins(texts,{ + c=0, -- Timer + text=GC.newText(FONT.get(floor(font/5)*5 or 40),text), -- String + x=x or 0, -- X + y=y or 0, -- Y + spd=(spd or 1), -- Timing speed(1=last 1 sec) + stop=stop, -- Stop time(sustained text) + draw=assert(textFX[style or 'appear'],"no text type:"..style),-- Draw method + }) +end +function TEXT.getText(text,x,y,font,style,spd,stop)-- Another version of TEXT.show(), but only return text object, need manual management + return { + c=0, + text=GC.newText(FONT.get(floor(font/5)*5 or 40),text), + x=x or 0, + y=y or 0, + spd=(spd or 1), + stop=stop, + draw=textFX[style or 'appear'] or error("unavailable type:"..style), + } +end +function TEXT.update(dt,list) + if not list then + list=texts + end + for i=#list,1,-1 do + local t=list[i] + t.c=t.c+t.spd*dt + if t.stop then + if t.c>t.stop then + t.c=t.stop + end + end + if t.c>1 then + rem(list,i) + end + end +end +function TEXT.draw(list) + if not list then + list=texts + end + for i=1,#list do + local t=list[i] + local p=t.c + setColor(1,1,1,p<.2 and p*5 or p<.8 and 1 or 5-p*5) + t:draw() + end +end +return TEXT diff --git a/Zframework/upcaser.txt b/Zframework/upcaser.txt new file mode 100644 index 000000000..cc6edd0d8 --- /dev/null +++ b/Zframework/upcaser.txt @@ -0,0 +1,26 @@ +a=A,b=B,c=C,d=D,e=E,f=F,g=G,h=H,i=I,j=J,k=K,l=L,m=M,n=N,o=O,p=P,q=Q,r=R,s=S,t=T,u=U,v=V,w=W,x=X,y=Y,z=Z +µ=Μ,ß=SS,à=À,á=Á,â=Â,ã=Ã,ä=Ä,å=Å,æ=Æ,ç=Ç,è=È,é=É,ê=Ê,ë=Ë,ì=Ì,í=Í,î=Î,ï=Ï,ð=Ð,ñ=Ñ,ò=Ò,ó=Ó,ô=Ô,õ=Õ,ö=Ö,ø=Ø,ù=Ù,ú=Ú,û=Û,ü=Ü,ý=Ý,þ=Þ,ÿ=Ÿ,ā=Ā,ă=Ă,ą=Ą,ć=Ć,ĉ=Ĉ,ċ=Ċ,č=Č,ď=Ď,đ=Đ,ē=Ē,ĕ=Ĕ,ė=Ė,ę=Ę,ě=Ě,ĝ=Ĝ,ğ=Ğ,ġ=Ġ,ģ=Ģ,ĥ=Ĥ,ħ=Ħ,ĩ=Ĩ,ī=Ī,ĭ=Ĭ,į=Į,ij=IJ,ĵ=Ĵ,ķ=Ķ,ĺ=Ĺ,ļ=Ļ,ľ=Ľ,ŀ=Ŀ,ł=Ł,ń=Ń,ņ=Ņ,ň=Ň,ŋ=Ŋ,ō=Ō,ŏ=Ŏ,ő=Ő,œ=Œ,ŕ=Ŕ,ŗ=Ŗ,ř=Ř,ś=Ś,ŝ=Ŝ,ş=Ş,š=Š,ţ=Ţ,ť=Ť,ŧ=Ŧ,ũ=Ũ,ū=Ū,ŭ=Ŭ,ů=Ů,ű=Ű,ų=Ų,ŵ=Ŵ,ŷ=Ŷ,ź=Ź,ż=Ż,ž=Ž +ƀ=Ƀ,ƃ=Ƃ,ƅ=Ƅ,ƈ=Ƈ,ƌ=Ƌ,ƒ=Ƒ,ƕ=Ƕ,ƙ=Ƙ,ƚ=Ƚ,ƞ=Ƞ,ơ=Ơ,ƣ=Ƣ,ƥ=Ƥ,ƨ=Ƨ,ƭ=Ƭ,ư=Ư,ƴ=Ƴ,ƶ=Ƶ,ƹ=Ƹ,ƽ=Ƽ,ƿ=Ƿ,dž=DŽ,lj=LJ,nj=NJ,ǎ=Ǎ,ǐ=Ǐ,ǒ=Ǒ,ǔ=Ǔ,ǖ=Ǖ,ǘ=Ǘ,ǚ=Ǚ,ǜ=Ǜ,ǝ=Ǝ,ǟ=Ǟ,ǡ=Ǡ,ǣ=Ǣ,ǥ=Ǥ,ǧ=Ǧ,ǩ=Ǩ,ǫ=Ǫ,ǭ=Ǭ,ǯ=Ǯ,dz=DZ,ǵ=Ǵ,ǹ=Ǹ,ǻ=Ǻ,ǽ=Ǽ,ǿ=Ǿ,ȁ=Ȁ,ȃ=Ȃ,ȅ=Ȅ,ȇ=Ȇ,ȉ=Ȉ,ȋ=Ȋ,ȍ=Ȍ,ȏ=Ȏ,ȑ=Ȑ,ȓ=Ȓ,ȕ=Ȕ,ȗ=Ȗ,ș=Ș,ț=Ț,ȝ=Ȝ,ȟ=Ȟ,ȣ=Ȣ,ȥ=Ȥ,ȧ=Ȧ,ȩ=Ȩ,ȫ=Ȫ,ȭ=Ȭ,ȯ=Ȯ,ȱ=Ȱ,ȳ=Ȳ,ȼ=Ȼ,ȿ=Ȿ,ɀ=Ɀ,ɂ=Ɂ,ɇ=Ɇ,ɉ=Ɉ,ɋ=Ɋ,ɍ=Ɍ,ɏ=Ɏ +ɐ=Ɐ,ɑ=Ɑ,ɒ=Ɒ,ɓ=Ɓ,ɔ=Ɔ,ɖ=Ɖ,ɗ=Ɗ,ə=Ə,ɛ=Ɛ,ɜ=Ɜ,ɠ=Ɠ,ɡ=Ɡ,ɣ=Ɣ,ɥ=Ɥ,ɦ=Ɦ,ɨ=Ɨ,ɩ=Ɩ,ɪ=Ɪ,ɫ=Ɫ,ɬ=Ɬ,ɯ=Ɯ,ɱ=Ɱ,ɲ=Ɲ,ɵ=Ɵ,ɽ=Ɽ,ʀ=Ʀ,ʂ=Ʂ,ʃ=Ʃ,ʇ=Ʇ,ʈ=Ʈ,ʉ=Ʉ,ʊ=Ʊ,ʋ=Ʋ,ʌ=Ʌ,ʒ=Ʒ,ʝ=Ʝ,ʞ=Ʞ +ͱ=Ͱ,ͳ=Ͳ,ͷ=Ͷ,ͻ=Ͻ,ͼ=Ͼ,ͽ=Ͽ,ά=Ά,έ=Έ,ή=Ή,ί=Ί,α=Α,β=Β,γ=Γ,δ=Δ,ε=Ε,ζ=Ζ,η=Η,θ=Θ,κ=Κ,λ=Λ,ν=Ν,ξ=Ξ,ο=Ο,π=Π,ρ=Ρ,σ=Σ,τ=Τ,υ=Υ,φ=Φ,χ=Χ,ψ=Ψ,ω=Ω,ϊ=Ϊ,ϋ=Ϋ,ό=Ό,ύ=Ύ,ώ=Ώ,ϗ=Ϗ,ϙ=Ϙ,ϛ=Ϛ,ϝ=Ϝ,ϟ=Ϟ,ϡ=Ϡ,ϣ=Ϣ,ϥ=Ϥ,ϧ=Ϧ,ϩ=Ϩ,ϫ=Ϫ,ϭ=Ϭ,ϯ=Ϯ,ϲ=Ϲ,ϳ=Ϳ,ϸ=Ϸ,ϻ=Ϻ +а=А,б=Б,в=В,г=Г,д=Д,е=Е,ж=Ж,з=З,и=И,й=Й,к=К,л=Л,м=М,н=Н,о=О,п=П,р=Р,с=С,т=Т,у=У,ф=Ф,х=Х,ц=Ц,ч=Ч,ш=Ш,щ=Щ,ъ=Ъ,ы=Ы,ь=Ь,э=Э,ю=Ю,я=Я,ѐ=Ѐ,ё=Ё,ђ=Ђ,ѓ=Ѓ,є=Є,ѕ=Ѕ,і=І,ї=Ї,ј=Ј,љ=Љ,њ=Њ,ћ=Ћ,ќ=Ќ,ѝ=Ѝ,ў=Ў,џ=Џ,ѡ=Ѡ,ѣ=Ѣ,ѥ=Ѥ,ѧ=Ѧ,ѩ=Ѩ,ѫ=Ѫ,ѭ=Ѭ,ѯ=Ѯ,ѱ=Ѱ,ѳ=Ѳ,ѵ=Ѵ,ѷ=Ѷ,ѹ=Ѹ,ѻ=Ѻ,ѽ=Ѽ,ѿ=Ѿ,ҁ=Ҁ,ҋ=Ҋ,ҍ=Ҍ,ҏ=Ҏ,ґ=Ґ,ғ=Ғ,ҕ=Ҕ,җ=Җ,ҙ=Ҙ,қ=Қ,ҝ=Ҝ,ҟ=Ҟ,ҡ=Ҡ,ң=Ң,ҥ=Ҥ,ҧ=Ҧ,ҩ=Ҩ,ҫ=Ҫ,ҭ=Ҭ,ү=Ү,ұ=Ұ,ҳ=Ҳ,ҵ=Ҵ,ҷ=Ҷ,ҹ=Ҹ,һ=Һ,ҽ=Ҽ,ҿ=Ҿ,ӂ=Ӂ,ӄ=Ӄ,ӆ=Ӆ,ӈ=Ӈ,ӊ=Ӊ,ӌ=Ӌ,ӎ=Ӎ,ӏ=Ӏ,ӑ=Ӑ,ӓ=Ӓ,ӕ=Ӕ,ӗ=Ӗ,ә=Ә,ӛ=Ӛ,ӝ=Ӝ,ӟ=Ӟ,ӡ=Ӡ,ӣ=Ӣ,ӥ=Ӥ,ӧ=Ӧ,ө=Ө,ӫ=Ӫ,ӭ=Ӭ,ӯ=Ӯ,ӱ=Ӱ,ӳ=Ӳ,ӵ=Ӵ,ӷ=Ӷ,ӹ=Ӹ,ӻ=Ӻ,ӽ=Ӽ,ӿ=Ӿ,ԁ=Ԁ,ԃ=Ԃ,ԅ=Ԅ,ԇ=Ԇ,ԉ=Ԉ,ԋ=Ԋ,ԍ=Ԍ,ԏ=Ԏ,ԑ=Ԑ,ԓ=Ԓ,ԕ=Ԕ,ԗ=Ԗ,ԙ=Ԙ,ԛ=Ԛ,ԝ=Ԝ,ԟ=Ԟ,ԡ=Ԡ,ԣ=Ԣ,ԥ=Ԥ,ԧ=Ԧ,ԩ=Ԩ,ԫ=Ԫ,ԭ=Ԭ,ԯ=Ԯ +ա=Ա,բ=Բ,գ=Գ,դ=Դ,ե=Ե,զ=Զ,է=Է,ը=Ը,թ=Թ,ժ=Ժ,ի=Ի,լ=Լ,խ=Խ,ծ=Ծ,կ=Կ,հ=Հ,ձ=Ձ,ղ=Ղ,ճ=Ճ,մ=Մ,յ=Յ,ն=Ն,շ=Շ,ո=Ո,չ=Չ,պ=Պ,ջ=Ջ,ռ=Ռ,ս=Ս,վ=Վ,տ=Տ,ր=Ր,ց=Ց,ւ=Ւ,փ=Փ,ք=Ք,օ=Օ,ֆ=Ֆ +ა=Ა,ბ=Ბ,გ=Გ,დ=Დ,ე=Ე,ვ=Ვ,ზ=Ზ,თ=Თ,ი=Ი,კ=Კ,ლ=Ლ,მ=Მ,ნ=Ნ,ო=Ო,პ=Პ,ჟ=Ჟ,რ=Რ,ს=Ს,ტ=Ტ,უ=Უ,ფ=Ფ,ქ=Ქ,ღ=Ღ,ყ=Ყ,შ=Შ,ჩ=Ჩ,ც=Ც,ძ=Ძ,წ=Წ,ჭ=Ჭ,ხ=Ხ,ჯ=Ჯ,ჰ=Ჰ,ჱ=Ჱ,ჲ=Ჲ,ჳ=Ჳ,ჴ=Ჴ,ჵ=Ჵ,ჶ=Ჶ,ჷ=Ჷ,ჸ=Ჸ,ჹ=Ჹ,ჺ=Ჺ,ჽ=Ჽ,ჾ=Ჾ,ჿ=Ჿ +ᏸ=Ᏸ,ᏹ=Ᏹ,ᏺ=Ᏺ,ᏻ=Ᏻ,ᏼ=Ᏼ,ᏽ=Ᏽ +ᲈ=Ꙋ,ᵹ=Ᵹ,ᵽ=Ᵽ,ᶎ=Ᶎ,ḁ=Ḁ,ḃ=Ḃ,ḅ=Ḅ,ḇ=Ḇ,ḉ=Ḉ,ḋ=Ḋ,ḍ=Ḍ,ḏ=Ḏ,ḑ=Ḑ,ḓ=Ḓ,ḕ=Ḕ,ḗ=Ḗ,ḙ=Ḙ,ḛ=Ḛ,ḝ=Ḝ,ḟ=Ḟ,ḡ=Ḡ,ḣ=Ḣ,ḥ=Ḥ,ḧ=Ḧ,ḩ=Ḩ,ḫ=Ḫ,ḭ=Ḭ,ḯ=Ḯ,ḱ=Ḱ,ḳ=Ḳ,ḵ=Ḵ,ḷ=Ḷ,ḹ=Ḹ,ḻ=Ḻ,ḽ=Ḽ,ḿ=Ḿ,ṁ=Ṁ,ṃ=Ṃ,ṅ=Ṅ,ṇ=Ṇ,ṉ=Ṉ,ṋ=Ṋ,ṍ=Ṍ,ṏ=Ṏ,ṑ=Ṑ,ṓ=Ṓ,ṕ=Ṕ,ṗ=Ṗ,ṙ=Ṙ,ṛ=Ṛ,ṝ=Ṝ,ṟ=Ṟ,ṡ=Ṡ,ṣ=Ṣ,ṥ=Ṥ,ṧ=Ṧ,ṩ=Ṩ,ṫ=Ṫ,ṭ=Ṭ,ṯ=Ṯ,ṱ=Ṱ,ṳ=Ṳ,ṵ=Ṵ,ṷ=Ṷ,ṹ=Ṹ,ṻ=Ṻ,ṽ=Ṽ,ṿ=Ṿ,ẁ=Ẁ,ẃ=Ẃ,ẅ=Ẅ,ẇ=Ẇ,ẉ=Ẉ,ẋ=Ẋ,ẍ=Ẍ,ẏ=Ẏ,ẑ=Ẑ,ẓ=Ẓ,ẕ=Ẕ,ạ=Ạ,ả=Ả,ấ=Ấ,ầ=Ầ,ẩ=Ẩ,ẫ=Ẫ,ậ=Ậ,ắ=Ắ,ằ=Ằ,ẳ=Ẳ,ẵ=Ẵ,ặ=Ặ,ẹ=Ẹ,ẻ=Ẻ,ẽ=Ẽ,ế=Ế,ề=Ề,ể=Ể,ễ=Ễ,ệ=Ệ,ỉ=Ỉ,ị=Ị,ọ=Ọ,ỏ=Ỏ,ố=Ố,ồ=Ồ,ổ=Ổ,ỗ=Ỗ,ộ=Ộ,ớ=Ớ,ờ=Ờ,ở=Ở,ỡ=Ỡ,ợ=Ợ,ụ=Ụ,ủ=Ủ,ứ=Ứ,ừ=Ừ,ử=Ử,ữ=Ữ,ự=Ự,ỳ=Ỳ,ỵ=Ỵ,ỷ=Ỷ,ỹ=Ỹ,ỻ=Ỻ,ỽ=Ỽ,ỿ=Ỿ,ἀ=Ἀ,ἁ=Ἁ,ἂ=Ἂ,ἃ=Ἃ,ἄ=Ἄ,ἅ=Ἅ,ἆ=Ἆ,ἇ=Ἇ,ἐ=Ἐ,ἑ=Ἑ,ἒ=Ἒ,ἓ=Ἓ,ἔ=Ἔ,ἕ=Ἕ,ἠ=Ἠ,ἡ=Ἡ,ἢ=Ἢ,ἣ=Ἣ,ἤ=Ἤ,ἥ=Ἥ,ἦ=Ἦ,ἧ=Ἧ,ἰ=Ἰ,ἱ=Ἱ,ἲ=Ἲ,ἳ=Ἳ,ἴ=Ἴ,ἵ=Ἵ,ἶ=Ἶ,ἷ=Ἷ,ὀ=Ὀ,ὁ=Ὁ,ὂ=Ὂ,ὃ=Ὃ,ὄ=Ὄ,ὅ=Ὅ,ὑ=Ὑ,ὓ=Ὓ,ὕ=Ὕ,ὗ=Ὗ,ὠ=Ὠ,ὡ=Ὡ,ὢ=Ὢ,ὣ=Ὣ,ὤ=Ὤ,ὥ=Ὥ,ὦ=Ὦ,ὧ=Ὧ,ὰ=Ὰ,ά=Ά,ὲ=Ὲ,έ=Έ,ὴ=Ὴ,ή=Ή,ὶ=Ὶ,ί=Ί,ὸ=Ὸ,ό=Ό,ὺ=Ὺ,ύ=Ύ,ὼ=Ὼ,ώ=Ώ,ᾰ=Ᾰ,ᾱ=Ᾱ,ῐ=Ῐ,ῑ=Ῑ,ῠ=Ῠ,ῡ=Ῡ,ῥ=Ῥ,ⅎ=Ⅎ +ⅰ=Ⅰ,ⅱ=Ⅱ,ⅲ=Ⅲ,ⅳ=Ⅳ,ⅴ=Ⅴ,ⅵ=Ⅵ,ⅶ=Ⅶ,ⅷ=Ⅷ,ⅸ=Ⅸ,ⅹ=Ⅹ,ⅺ=Ⅺ,ⅻ=Ⅻ,ⅼ=Ⅼ,ⅽ=Ⅽ,ⅾ=Ⅾ,ⅿ=Ⅿ,ↄ=Ↄ +ⓐ=Ⓐ,ⓑ=Ⓑ,ⓒ=Ⓒ,ⓓ=Ⓓ,ⓔ=Ⓔ,ⓕ=Ⓕ,ⓖ=Ⓖ,ⓗ=Ⓗ,ⓘ=Ⓘ,ⓙ=Ⓙ,ⓚ=Ⓚ,ⓛ=Ⓛ,ⓜ=Ⓜ,ⓝ=Ⓝ,ⓞ=Ⓞ,ⓟ=Ⓟ,ⓠ=Ⓠ,ⓡ=Ⓡ,ⓢ=Ⓢ,ⓣ=Ⓣ,ⓤ=Ⓤ,ⓥ=Ⓥ,ⓦ=Ⓦ,ⓧ=Ⓧ,ⓨ=Ⓨ,ⓩ=Ⓩ +ⰰ=Ⰰ,ⰱ=Ⰱ,ⰲ=Ⰲ,ⰳ=Ⰳ,ⰴ=Ⰴ,ⰵ=Ⰵ,ⰶ=Ⰶ,ⰷ=Ⰷ,ⰸ=Ⰸ,ⰹ=Ⰹ,ⰺ=Ⰺ,ⰻ=Ⰻ,ⰼ=Ⰼ,ⰽ=Ⰽ,ⰾ=Ⰾ,ⰿ=Ⰿ,ⱀ=Ⱀ,ⱁ=Ⱁ,ⱂ=Ⱂ,ⱃ=Ⱃ,ⱄ=Ⱄ,ⱅ=Ⱅ,ⱆ=Ⱆ,ⱇ=Ⱇ,ⱈ=Ⱈ,ⱉ=Ⱉ,ⱊ=Ⱊ,ⱋ=Ⱋ,ⱌ=Ⱌ,ⱍ=Ⱍ,ⱎ=Ⱎ,ⱏ=Ⱏ,ⱐ=Ⱐ,ⱑ=Ⱑ,ⱒ=Ⱒ,ⱓ=Ⱓ,ⱔ=Ⱔ,ⱕ=Ⱕ,ⱖ=Ⱖ,ⱗ=Ⱗ,ⱘ=Ⱘ,ⱙ=Ⱙ,ⱚ=Ⱚ,ⱛ=Ⱛ,ⱜ=Ⱜ,ⱝ=Ⱝ,ⱞ=Ⱞ,ⱟ=Ⱟ +ⱡ=Ⱡ,ⱥ=Ⱥ,ⱦ=Ⱦ,ⱨ=Ⱨ,ⱪ=Ⱪ,ⱬ=Ⱬ,ⱳ=Ⱳ,ⱶ=Ⱶ +ⲁ=Ⲁ,ⲃ=Ⲃ,ⲅ=Ⲅ,ⲇ=Ⲇ,ⲉ=Ⲉ,ⲋ=Ⲋ,ⲍ=Ⲍ,ⲏ=Ⲏ,ⲑ=Ⲑ,ⲓ=Ⲓ,ⲕ=Ⲕ,ⲗ=Ⲗ,ⲙ=Ⲙ,ⲛ=Ⲛ,ⲝ=Ⲝ,ⲟ=Ⲟ,ⲡ=Ⲡ,ⲣ=Ⲣ,ⲥ=Ⲥ,ⲧ=Ⲧ,ⲩ=Ⲩ,ⲫ=Ⲫ,ⲭ=Ⲭ,ⲯ=Ⲯ,ⲱ=Ⲱ,ⲳ=Ⲳ,ⲵ=Ⲵ,ⲷ=Ⲷ,ⲹ=Ⲹ,ⲻ=Ⲻ,ⲽ=Ⲽ,ⲿ=Ⲿ,ⳁ=Ⳁ,ⳃ=Ⳃ,ⳅ=Ⳅ,ⳇ=Ⳇ,ⳉ=Ⳉ,ⳋ=Ⳋ,ⳍ=Ⳍ,ⳏ=Ⳏ,ⳑ=Ⳑ,ⳓ=Ⳓ,ⳕ=Ⳕ,ⳗ=Ⳗ,ⳙ=Ⳙ,ⳛ=Ⳛ,ⳝ=Ⳝ,ⳟ=Ⳟ,ⳡ=Ⳡ,ⳣ=Ⳣ,ⳬ=Ⳬ,ⳮ=Ⳮ,ⳳ=Ⳳ +ⴀ=Ⴀ,ⴁ=Ⴁ,ⴂ=Ⴂ,ⴃ=Ⴃ,ⴄ=Ⴄ,ⴅ=Ⴅ,ⴆ=Ⴆ,ⴇ=Ⴇ,ⴈ=Ⴈ,ⴉ=Ⴉ,ⴊ=Ⴊ,ⴋ=Ⴋ,ⴌ=Ⴌ,ⴍ=Ⴍ,ⴎ=Ⴎ,ⴏ=Ⴏ,ⴐ=Ⴐ,ⴑ=Ⴑ,ⴒ=Ⴒ,ⴓ=Ⴓ,ⴔ=Ⴔ,ⴕ=Ⴕ,ⴖ=Ⴖ,ⴗ=Ⴗ,ⴘ=Ⴘ,ⴙ=Ⴙ,ⴚ=Ⴚ,ⴛ=Ⴛ,ⴜ=Ⴜ,ⴝ=Ⴝ,ⴞ=Ⴞ,ⴟ=Ⴟ,ⴠ=Ⴠ,ⴡ=Ⴡ,ⴢ=Ⴢ,ⴣ=Ⴣ,ⴤ=Ⴤ,ⴥ=Ⴥ,ⴧ=Ⴧ +ⴭ=Ⴭ,ꙁ=Ꙁ,ꙃ=Ꙃ,ꙅ=Ꙅ,ꙇ=Ꙇ,ꙉ=Ꙉ,ꙍ=Ꙍ,ꙏ=Ꙏ,ꙑ=Ꙑ,ꙓ=Ꙓ,ꙕ=Ꙕ,ꙗ=Ꙗ,ꙙ=Ꙙ,ꙛ=Ꙛ,ꙝ=Ꙝ,ꙟ=Ꙟ,ꙡ=Ꙡ,ꙣ=Ꙣ,ꙥ=Ꙥ,ꙧ=Ꙧ,ꙩ=Ꙩ,ꙫ=Ꙫ,ꙭ=Ꙭ,ꚁ=Ꚁ,ꚃ=Ꚃ,ꚅ=Ꚅ,ꚇ=Ꚇ,ꚉ=Ꚉ,ꚋ=Ꚋ,ꚍ=Ꚍ,ꚏ=Ꚏ,ꚑ=Ꚑ,ꚓ=Ꚓ,ꚕ=Ꚕ,ꚗ=Ꚗ,ꚙ=Ꚙ,ꚛ=Ꚛ,ꜣ=Ꜣ,ꜥ=Ꜥ,ꜧ=Ꜧ,ꜩ=Ꜩ,ꜫ=Ꜫ,ꜭ=Ꜭ,ꜯ=Ꜯ,ꜳ=Ꜳ,ꜵ=Ꜵ,ꜷ=Ꜷ,ꜹ=Ꜹ,ꜻ=Ꜻ,ꜽ=Ꜽ,ꜿ=Ꜿ,ꝁ=Ꝁ,ꝃ=Ꝃ,ꝅ=Ꝅ,ꝇ=Ꝇ,ꝉ=Ꝉ,ꝋ=Ꝋ,ꝍ=Ꝍ,ꝏ=Ꝏ,ꝑ=Ꝑ,ꝓ=Ꝓ,ꝕ=Ꝕ,ꝗ=Ꝗ,ꝙ=Ꝙ,ꝛ=Ꝛ,ꝝ=Ꝝ,ꝟ=Ꝟ,ꝡ=Ꝡ,ꝣ=Ꝣ,ꝥ=Ꝥ,ꝧ=Ꝧ,ꝩ=Ꝩ,ꝫ=Ꝫ,ꝭ=Ꝭ,ꝯ=Ꝯ,ꝺ=Ꝺ,ꝼ=Ꝼ,ꝿ=Ꝿ,ꞁ=Ꞁ,ꞃ=Ꞃ,ꞅ=Ꞅ,ꞇ=Ꞇ,ꞌ=Ꞌ,ꞑ=Ꞑ,ꞓ=Ꞓ,ꞔ=Ꞔ,ꞗ=Ꞗ,ꞙ=Ꞙ,ꞛ=Ꞛ,ꞝ=Ꞝ,ꞟ=Ꞟ,ꞡ=Ꞡ,ꞣ=Ꞣ,ꞥ=Ꞥ,ꞧ=Ꞧ,ꞩ=Ꞩ,ꞵ=Ꞵ,ꞷ=Ꞷ,ꞹ=Ꞹ,ꞻ=Ꞻ,ꞽ=Ꞽ,ꞿ=Ꞿ,ꟁ=Ꟁ,ꟃ=Ꟃ,ꟈ=Ꟈ,ꟊ=Ꟊ,ꟑ=Ꟑ,ꟗ=Ꟗ,ꟙ=Ꟙ,ꟶ=Ꟶ,ꭓ=Ꭓ +ꭰ=Ꭰ,ꭱ=Ꭱ,ꭲ=Ꭲ,ꭳ=Ꭳ,ꭴ=Ꭴ,ꭵ=Ꭵ,ꭶ=Ꭶ,ꭷ=Ꭷ,ꭸ=Ꭸ,ꭹ=Ꭹ,ꭺ=Ꭺ,ꭻ=Ꭻ,ꭼ=Ꭼ,ꭽ=Ꭽ,ꭾ=Ꭾ,ꭿ=Ꭿ,ꮀ=Ꮀ,ꮁ=Ꮁ,ꮂ=Ꮂ,ꮃ=Ꮃ,ꮄ=Ꮄ,ꮅ=Ꮅ,ꮆ=Ꮆ,ꮇ=Ꮇ,ꮈ=Ꮈ,ꮉ=Ꮉ,ꮊ=Ꮊ,ꮋ=Ꮋ,ꮌ=Ꮌ,ꮍ=Ꮍ,ꮎ=Ꮎ,ꮏ=Ꮏ,ꮐ=Ꮐ,ꮑ=Ꮑ,ꮒ=Ꮒ,ꮓ=Ꮓ,ꮔ=Ꮔ,ꮕ=Ꮕ,ꮖ=Ꮖ,ꮗ=Ꮗ,ꮘ=Ꮘ,ꮙ=Ꮙ,ꮚ=Ꮚ,ꮛ=Ꮛ,ꮜ=Ꮜ,ꮝ=Ꮝ,ꮞ=Ꮞ,ꮟ=Ꮟ,ꮠ=Ꮠ,ꮡ=Ꮡ,ꮢ=Ꮢ,ꮣ=Ꮣ,ꮤ=Ꮤ,ꮥ=Ꮥ,ꮦ=Ꮦ,ꮧ=Ꮧ,ꮨ=Ꮨ,ꮩ=Ꮩ,ꮪ=Ꮪ,ꮫ=Ꮫ,ꮬ=Ꮬ,ꮭ=Ꮭ,ꮮ=Ꮮ,ꮯ=Ꮯ,ꮰ=Ꮰ,ꮱ=Ꮱ,ꮲ=Ꮲ,ꮳ=Ꮳ,ꮴ=Ꮴ,ꮵ=Ꮵ,ꮶ=Ꮶ,ꮷ=Ꮷ,ꮸ=Ꮸ,ꮹ=Ꮹ,ꮺ=Ꮺ,ꮻ=Ꮻ,ꮼ=Ꮼ,ꮽ=Ꮽ,ꮾ=Ꮾ,ꮿ=Ꮿ +a=A,b=B,c=C,d=D,e=E,f=F,g=G,h=H,i=I,j=J,k=K,l=L,m=M,n=N,o=O,p=P,q=Q,r=R,s=S,t=T,u=U,v=V,w=W,x=X,y=Y,z=Z +𐐨=𐐀,𐐩=𐐁,𐐪=𐐂,𐐫=𐐃,𐐬=𐐄,𐐭=𐐅,𐐮=𐐆,𐐯=𐐇,𐐰=𐐈,𐐱=𐐉,𐐲=𐐊,𐐳=𐐋,𐐴=𐐌,𐐵=𐐍,𐐶=𐐎,𐐷=𐐏,𐐸=𐐐,𐐹=𐐑,𐐺=𐐒,𐐻=𐐓,𐐼=𐐔,𐐽=𐐕,𐐾=𐐖,𐐿=𐐗,𐑀=𐐘,𐑁=𐐙,𐑂=𐐚,𐑃=𐐛,𐑄=𐐜,𐑅=𐐝,𐑆=𐐞,𐑇=𐐟,𐑈=𐐠,𐑉=𐐡,𐑊=𐐢,𐑋=𐐣,𐑌=𐐤,𐑍=𐐥,𐑎=𐐦,𐑏=𐐧 +𐓘=𐒰,𐓙=𐒱,𐓚=𐒲,𐓛=𐒳,𐓜=𐒴,𐓝=𐒵,𐓞=𐒶,𐓟=𐒷,𐓠=𐒸,𐓡=𐒹,𐓢=𐒺,𐓣=𐒻,𐓤=𐒼,𐓥=𐒽,𐓦=𐒾,𐓧=𐒿,𐓨=𐓀,𐓩=𐓁,𐓪=𐓂,𐓫=𐓃,𐓬=𐓄,𐓭=𐓅,𐓮=𐓆,𐓯=𐓇,𐓰=𐓈,𐓱=𐓉,𐓲=𐓊,𐓳=𐓋,𐓴=𐓌,𐓵=𐓍,𐓶=𐓎,𐓷=𐓏,𐓸=𐓐,𐓹=𐓑,𐓺=𐓒,𐓻=𐓓 +𐳀=𐲀,𐳁=𐲁,𐳂=𐲂,𐳃=𐲃,𐳄=𐲄,𐳅=𐲅,𐳆=𐲆,𐳇=𐲇,𐳈=𐲈,𐳉=𐲉,𐳊=𐲊,𐳋=𐲋,𐳌=𐲌,𐳍=𐲍,𐳎=𐲎,𐳏=𐲏,𐳐=𐲐,𐳑=𐲑,𐳒=𐲒,𐳓=𐲓,𐳔=𐲔,𐳕=𐲕,𐳖=𐲖,𐳗=𐲗,𐳘=𐲘,𐳙=𐲙,𐳚=𐲚,𐳛=𐲛,𐳜=𐲜,𐳝=𐲝,𐳞=𐲞,𐳟=𐲟,𐳠=𐲠,𐳡=𐲡,𐳢=𐲢,𐳣=𐲣,𐳤=𐲤,𐳥=𐲥,𐳦=𐲦,𐳧=𐲧,𐳨=𐲨,𐳩=𐲩,𐳪=𐲪,𐳫=𐲫,𐳬=𐲬,𐳭=𐲭,𐳮=𐲮,𐳯=𐲯,𐳰=𐲰,𐳱=𐲱,𐳲=𐲲 +𑣀=𑢠,𑣁=𑢡,𑣂=𑢢,𑣃=𑢣,𑣄=𑢤,𑣅=𑢥,𑣆=𑢦,𑣇=𑢧,𑣈=𑢨,𑣉=𑢩,𑣊=𑢪,𑣋=𑢫,𑣌=𑢬,𑣍=𑢭,𑣎=𑢮,𑣏=𑢯,𑣐=𑢰,𑣑=𑢱,𑣒=𑢲,𑣓=𑢳,𑣔=𑢴,𑣕=𑢵,𑣖=𑢶,𑣗=𑢷,𑣘=𑢸,𑣙=𑢹,𑣚=𑢺,𑣛=𑢻,𑣜=𑢼,𑣝=𑢽,𑣞=𑢾,𑣟=𑢿 +𖹠=𖹀,𖹡=𖹁,𖹢=𖹂,𖹣=𖹃,𖹤=𖹄,𖹥=𖹅,𖹦=𖹆,𖹧=𖹇,𖹨=𖹈,𖹩=𖹉,𖹪=𖹊,𖹫=𖹋,𖹬=𖹌,𖹭=𖹍,𖹮=𖹎,𖹯=𖹏,𖹰=𖹐,𖹱=𖹑,𖹲=𖹒,𖹳=𖹓,𖹴=𖹔,𖹵=𖹕,𖹶=𖹖,𖹷=𖹗,𖹸=𖹘,𖹹=𖹙,𖹺=𖹚,𖹻=𖹛,𖹼=𖹜,𖹽=𖹝,𖹾=𖹞,𖹿=𖹟 +𞤢=𞤀,𞤣=𞤁,𞤤=𞤂,𞤥=𞤃,𞤦=𞤄,𞤧=𞤅,𞤨=𞤆,𞤩=𞤇,𞤪=𞤈,𞤫=𞤉,𞤬=𞤊,𞤭=𞤋,𞤮=𞤌,𞤯=𞤍,𞤰=𞤎,𞤱=𞤏,𞤲=𞤐,𞤳=𞤑,𞤴=𞤒,𞤵=𞤓,𞤶=𞤔,𞤷=𞤕,𞤸=𞤖,𞤹=𞤗,𞤺=𞤘,𞤻=𞤙,𞤼=𞤚,𞤽=𞤛,𞤾=𞤜,𞤿=𞤝,𞥀=𞤞,𞥁=𞤟,𞥂=𞤠,𞥃=𞤡 +i̇=İ,ʼn=ʼN,ǰ=J̌,ΐ=Ϊ́,ΰ=Ϋ́,և=ԵՒ,ẖ=H̱,ẗ=T̈,ẘ=W̊,ẙ=Y̊,ẚ=Aʾ,ὐ=Υ̓,ὒ=Υ̓̀,ὔ=Υ̓́,ὖ=Υ̓͂,ᾀ=ἈΙ,ᾁ=ἉΙ,ᾂ=ἊΙ,ᾃ=ἋΙ,ᾄ=ἌΙ,ᾅ=ἍΙ,ᾆ=ἎΙ,ᾇ=ἏΙ,ᾐ=ἨΙ,ᾑ=ἩΙ,ᾒ=ἪΙ,ᾓ=ἫΙ,ᾔ=ἬΙ,ᾕ=ἭΙ,ᾖ=ἮΙ,ᾗ=ἯΙ,ᾠ=ὨΙ,ᾡ=ὩΙ,ᾢ=ὪΙ,ᾣ=ὫΙ,ᾤ=ὬΙ,ᾥ=ὭΙ,ᾦ=ὮΙ,ᾧ=ὯΙ,ᾲ=ᾺΙ,ᾳ=ΑΙ,ᾴ=ΆΙ,ᾶ=Α͂,ᾷ=Α͂Ι,ῂ=ῊΙ,ῃ=ΗΙ,ῄ=ΉΙ,ῆ=Η͂,ῇ=Η͂Ι,ῒ=Ϊ̀,ῖ=Ι͂,ῗ=Ϊ͂,ῢ=Ϋ̀,ῤ=Ρ̓,ῦ=Υ͂,ῧ=Ϋ͂,ῲ=ῺΙ,ῳ=ΩΙ,ῴ=ΏΙ,ῶ=Ω͂,ῷ=Ω͂Ι,ﬓ=ՄՆ,ﬔ=ՄԵ,ﬕ=ՄԻ,ﬖ=ՎՆ,ﬗ=ՄԽ diff --git a/Zframework/vibrate.lua b/Zframework/vibrate.lua new file mode 100644 index 000000000..e4e28a6f4 --- /dev/null +++ b/Zframework/vibrate.lua @@ -0,0 +1,12 @@ +local level={0,0,.01,.016,.023,.03,.04,.05,.06,.07,.08,.09,.12,.15} +local vib=love.system.vibrate +return SYSTEM=='iOS' and + function(t) + t=level[t] + if t then vib(t<=.03 and 1 or t<=.09 and 2 or 3) end + end +or + function(t) + t=level[t] + if t then vib(t) end + end \ No newline at end of file diff --git a/Zframework/voice.lua b/Zframework/voice.lua new file mode 100644 index 000000000..3768ff766 --- /dev/null +++ b/Zframework/voice.lua @@ -0,0 +1,144 @@ +local rnd=math.random +local volume=1 +local diversion=0 +local voiceQueue={free=0} +local VOC={ + getCount=function() return 0 end, + getQueueCount=function() return 0 end, + load=function() error("Cannot load before init!") end, + getFreeChannel=NULL, + play=NULL, + update=NULL, +} +function VOC.setDiversion(n) + assert(type(n)=='number' and n>0 and n<12,'Wrong div') + diversion=n +end +function VOC.setVol(v) + assert(type(v)=='number' and v>=0 and v<=1,'Wrong volume') + volume=v + for i=1,#voiceQueue do + local Q=voiceQueue[i] + for j=1,#Q do + local s=Q[j] + if type(s)=='userdata' then + s:setVolume(volume) + end + end + end +end +function VOC.init(list) + VOC.init=nil + local rem=table.remove + local bank={}-- {vocName1={SRC1s},vocName2={SRC2s},...} + local Source={} + + local count=#list function VOC.getCount() return count end + local function _loadVoiceFile(path,N,vocName) + local fullPath=path..vocName..'.ogg' + if love.filesystem.getInfo(fullPath) then + bank[vocName]={love.audio.newSource(fullPath,'stream')} + table.insert(Source[N],vocName) + return true + end + end + -- Load voice with string + local function _getVoice(str) + local L=bank[str] + local n=1 + while L[n]:isPlaying() do + n=n+1 + if not L[n] then + L[n]=L[1]:clone() + L[n]:seek(0) + break + end + end + return L[n] + end + function VOC.load(path) + for i=1,count do + Source[list[i]]={} + + local n=0 + repeat n=n+1 until not _loadVoiceFile(path,list[i],list[i]..'_'..n) + + if n==1 then + if not _loadVoiceFile(path,list[i],list[i]) then + LOG("No VOC: "..list[i],.1) + end + end + if not Source[list[i]][1] then + Source[list[i]]=nil + end + end + + function VOC.getQueueCount() + return #voiceQueue + end + function VOC.getQueueLength(chn) + return voiceQueue[chn] and #voiceQueue[chn] + end + function VOC.getFreeChannel() + local l=#voiceQueue + for i=1,l do + if #voiceQueue[i]==0 then return i end + end + voiceQueue[l+1]={s=0} + return l+1 + end + + function VOC.play(s,chn) + if volume>0 then + local _=Source[s] + if not _ then return end + if chn then + local L=voiceQueue[chn] + L[#L+1]=_[rnd(#_)] + L.s=1 + -- Add to queue[chn] + else + voiceQueue[VOC.getFreeChannel()]={s=1,_[rnd(#_)]} + -- Create new channel & play + end + end + end + function VOC.update() + for i=#voiceQueue,1,-1 do + local Q=voiceQueue[i] + if Q.s==0 then-- Free channel, auto delete when >3 + if i>3 then + rem(voiceQueue,i) + end + elseif Q.s==1 then-- Waiting load source + Q[1]=_getVoice(Q[1]) + Q[1]:setVolume(volume) + Q[1]:setPitch(1.0594630943592953^(diversion*(rnd()*2-1))) + Q[1]:play() + Q.s=Q[2] and 2 or 4 + elseif Q.s==2 then-- Playing 1,ready 2 + if Q[1]:getDuration()-Q[1]:tell()<.08 then + Q[2]=_getVoice(Q[2]) + Q[2]:setVolume(volume) + Q[1]:setPitch(1.0594630943592953^(diversion*(rnd()*2-1))) + Q[2]:play() + Q.s=3 + end + elseif Q.s==3 then-- Playing 12 same time + if not Q[1]:isPlaying() then + for j=1,#Q do + Q[j]=Q[j+1] + end + Q.s=Q[2] and 2 or 4 + end + elseif Q.s==4 then-- Playing last + if not Q[1].isPlaying(Q[1]) then + Q[1]=nil + Q.s=0 + end + end + end + end + end +end +return VOC diff --git a/Zframework/wait.lua b/Zframework/wait.lua new file mode 100644 index 000000000..94aa6956b --- /dev/null +++ b/Zframework/wait.lua @@ -0,0 +1,128 @@ +local WAIT={ + state=false, + timer=false, + totalTimer=false, + + enterTime=.2, + leaveTime=.2, + timeout=6, + coverColor={.1,.1,.1}, + coverAlpha=.6, + + arg=false, +} + +local arcAlpha={1,.6,.4,.3} +local function defaultDraw(a,t) + GC.setLineWidth(SCR.h/26) + t=t*2.6 + for i=1,4 do + GC.setColor(1,1,1,a*arcAlpha[i]) + GC.arc('line','open',SCR.w/2,SCR.h/2,SCR.h/5,t+MATH.tau*(i/4),t+MATH.tau*((i+1)/4)) + end +end + +function WAIT.new(arg) + if WAIT.state then return end + + assert(type(arg)=='table',"arg must be table") + assert(arg.init==nil or type(arg.init) =='function',"Field 'enter' must be function") + assert(arg.update==nil or type(arg.update) =='function',"Field 'update' must be function") + assert(arg.quit==nil or type(arg.quit) =='function',"Field 'leave' must be function") + assert(arg.draw==nil or type(arg.draw) =='function',"Field 'draw' must be function") + assert(arg.escapable==nil or type(arg.escapable) =='boolean', "Field 'escapable' must be boolean") + if arg.init then arg.init() end + + WAIT.arg=arg + WAIT.state='enter' + WAIT.timer=0 + WAIT.totalTimer=0 +end + +function WAIT.interrupt() + if WAIT.state and WAIT.state~='leave' then + WAIT.state='leave' + WAIT.timer=WAIT.leaveTime*WAIT.timer/WAIT.enterTime + end +end + +function WAIT.update(dt) + if WAIT.state then + WAIT.totalTimer=WAIT.totalTimer+dt + if WAIT.arg.update then WAIT.arg.update(dt) end + + if WAIT.state~='leave' and WAIT.totalTimer>=(WAIT.arg.timeout or WAIT.timeout) then + WAIT.interrupt() + end + + if WAIT.state=='enter' then + WAIT.timer=math.min(WAIT.timer+dt,WAIT.enterTime) + if WAIT.timer>=WAIT.enterTime then WAIT.state='wait' end + elseif WAIT.state=='leave' then + WAIT.timer=WAIT.timer-dt + if WAIT.timer<=0 then + WAIT.state=false + if WAIT.arg.quit then WAIT.arg.quit() end + end + end + end +end + +function WAIT.draw() + if WAIT.state then + local alpha=( + WAIT.state=='enter' and WAIT.timer/WAIT.enterTime or + WAIT.state=='wait' and 1 or + WAIT.state=='leave' and WAIT.timer/WAIT.leaveTime + ) + GC.setColor( + WAIT.coverColor[1], + WAIT.coverColor[2], + WAIT.coverColor[3], + alpha*WAIT.coverAlpha + ) + GC.rectangle('fill',0,0,SCR.w,SCR.h); + + (WAIT.arg.draw or defaultDraw)(alpha,WAIT.totalTimer) + end +end + +function WAIT.setEnterTime(t) + assert(type(t)=='number' and t>0,"Arg #1 must be number larger then 0") + WAIT.enterTime=t +end +function WAIT.setLeaveTime(t) + assert(type(t)=='number' and t>0,"Arg #1 must be number larger then 0") + WAIT.leaveTime=t +end +function WAIT.setTimeout(t) + assert(type(t)=='number' and t>0,"Arg #1 must be number larger then 0") + WAIT.timeout=t +end +function WAIT.setCoverColor(r,g,b) + if type(r)=='table' then + r,g,b=r[1],r[2],r[3] + end + if + type(r)=='number' and r>=0 and r<=1 and + type(g)=='number' and g>=0 and g<=1 and + type(b)=='number' and b>=0 and b<=1 + then + WAIT.coverColor[1],WAIT.coverColor[2],WAIT.coverColor[3]=r,g,b + else + error("Arg must be r,g,b or {r,g,b}") + end +end +function WAIT.setCoverAlpha(a) + assert(type(a)=='number',"Arg #1 must be number between 0~1") +end +function WAIT.setDefaultDraw(f) + assert(type(f)=='function',"Arg #1 must be function") + defaultDraw=f +end + +setmetatable(WAIT,{__call=function(self,arg) + self.new(arg) +end,__metatable=true}) + +return WAIT diff --git a/Zframework/websocket.lua b/Zframework/websocket.lua new file mode 100644 index 000000000..d91d87d39 --- /dev/null +++ b/Zframework/websocket.lua @@ -0,0 +1,192 @@ +local host='127.0.0.1' +local port='80' +local path='' + +-- lua + LÖVE threading websocket client +-- Original pure lua ver. by flaribbit and Particle_G +-- Threading version by MrZ + +local type=type +local timer=love.timer.getTime + +local WS={} +local wsList=setmetatable({},{ + __index=function(l,k) + local ws={ + real=false, + status='dead', + lastPongTime=timer(), + sendTimer=0, + alertTimer=0, + pongTimer=0, + } + l[k]=ws + return ws + end +}) + +function WS.switchHost(_1,_2,_3) + for k in next,wsList do + WS.close(k) + end + host=_1 + port=_2 or port + path=_3 or path +end + +function WS.connect(name,subPath,head,timeout) + if head then + local l="" + for k,v in next,head do + l=l..(k..": "..v..'\r\n') + end + head=l + else + head="" + end + if wsList[name] and wsList[name].thread then + wsList[name].thread:release() + end + local ws={ + real=true, + thread=love.thread.newThread('Zframework/websocket_thread.lua'), + triggerCHN=love.thread.newChannel(), + sendCHN=love.thread.newChannel(), + readCHN=love.thread.newChannel(), + lastPingTime=0, + lastPongTime=timer(), + pingInterval=6, + status='connecting',-- 'connecting', 'running', 'dead' + sendTimer=0, + alertTimer=0, + pongTimer=0, + } + wsList[name]=ws + ws.thread:start(ws.triggerCHN,ws.sendCHN,ws.readCHN) + ws.sendCHN:push(host) + ws.sendCHN:push(port) + ws.sendCHN:push(path..subPath) + ws.sendCHN:push(head) + ws.sendCHN:push(timeout or 2.6) +end + +function WS.status(name) + local ws=wsList[name] + return ws.status or 'dead' +end + +function WS.getTimers(name) + local ws=wsList[name] + return ws.pongTimer,ws.sendTimer,ws.alertTimer +end + +function WS.setPingInterval(name,time) + local ws=wsList[name] + ws.pingInterval=math.max(time or 2.6,2.6) +end + +function WS.alert(name) + local ws=wsList[name] + ws.alertTimer=2.6 +end + +local OPcode={ + continue=0, + text=1, + binary=2, + close=8, + ping=9, + pong=10, +} +local OPname={ + [0]='continue', + [1]='text', + [2]='binary', + [8]='close', + [9]='ping', + [10]='pong', +} +function WS.send(name,message,op) + if type(message)=='string' then + local ws=wsList[name] + if ws.real and ws.status=='running' then + ws.sendCHN:push(op and OPcode[op] or 2)-- 2=binary + ws.sendCHN:push(message) + ws.lastPingTime=timer() + ws.sendTimer=1 + end + else + MES.new('error',"Attempt to send non-string value!") + MES.traceback() + end +end + +function WS.read(name) + local ws=wsList[name] + if ws.real and ws.status~='connecting' and ws.readCHN:getCount()>=2 then + local op,message=ws.readCHN:pop(),ws.readCHN:pop() + if op==8 then-- 8=close + ws.status='dead' + elseif op==9 then-- 9=ping + WS.send(name,message or "",'pong') + end + ws.lastPongTime=timer() + ws.pongTimer=1 + return message,OPname[op] or op + end +end + +function WS.close(name) + local ws=wsList[name] + if ws.real then + ws.sendCHN:push(8)-- 8=close + ws.sendCHN:push("") + ws.status='dead' + end +end + +function WS.update(dt) + local time=timer() + for name,ws in next,wsList do + if ws.real and ws.status~='dead' then + if ws.thread:isRunning() then + if ws.triggerCHN:getCount()==0 then + ws.triggerCHN:push(0) + end + if ws.status=='connecting' then + local mes=ws.readCHN:pop() + if mes then + if mes=='success' then + ws.status='running' + ws.lastPingTime=time + ws.lastPongTime=time + ws.pongTimer=1 + else + ws.status='dead' + MES.new('warn',text.wsFailed:repD(mes)) + end + end + elseif ws.status=='running' then + if time-ws.lastPingTime>ws.pingInterval then + WS.send(name,"",'pong') + end + if time-ws.lastPongTime>6+2*ws.pingInterval then + WS.close(name) + end + end + if ws.sendTimer>0 then ws.sendTimer=ws.sendTimer-dt end + if ws.pongTimer>0 then ws.pongTimer=ws.pongTimer-dt end + if ws.alertTimer>0 then ws.alertTimer=ws.alertTimer-dt end + else + ws.status='dead' + local err=ws.thread:getError() + if err then + MES.new('warn',text.wsClose:repD(err:match(":.-:(.-)\n"))) + WS.alert(name) + end + end + end + end +end + +return WS diff --git a/Zframework/websocket_thread.lua b/Zframework/websocket_thread.lua new file mode 100644 index 000000000..6588577dd --- /dev/null +++ b/Zframework/websocket_thread.lua @@ -0,0 +1,204 @@ +---@type love.Channel,love.Channel,love.Channel +local triggerCHN,sendCHN,readCHN=... + +local CHN_demand,CHN_getCount=triggerCHN.demand,triggerCHN.getCount +local CHN_push,CHN_pop=triggerCHN.push,triggerCHN.pop + +local SOCK=require'socket'.tcp() +local JSON=require'Zframework.json' +local sleep=require'love.timer'.sleep + +do-- Connect + -- Warning: workaround for love.js, used to use CHN_demand instead + while CHN_getCount(sendCHN)<5 do sleep(.0626) end + local host=CHN_pop(sendCHN) + local port=CHN_pop(sendCHN) + local path=CHN_pop(sendCHN) + local head=CHN_pop(sendCHN) + local timeout=CHN_pop(sendCHN) + + SOCK:settimeout(timeout) + local res,err=SOCK:connect(host,port) + -- print('C0',res,err) + assert(res,err) + + -- WebSocket handshake + SOCK:send( + 'GET '..path..' HTTP/1.1\r\n'.. + 'Host: '..host..':'..port..'\r\n'.. + 'Connection: Upgrade\r\n'.. + 'Upgrade: websocket\r\n'.. + 'Sec-WebSocket-Version: 13\r\n'.. + 'Sec-WebSocket-Key: osT3F7mvlojIvf3/8uIsJQ==\r\n'..-- secKey + head.. + '\r\n' + ) + + -- First line of HTTP + res,err=SOCK:receive('*l') + -- print('C',res,err) + assert(res,err) + local code,ctLen + code=res:find(' ') + code=res:sub(code+1,code+3) + + -- Get body length from headers and remove headers + repeat + res,err=SOCK:receive('*l') + -- print('H',res,err) + assert(res,err) + if not ctLen and res:find('content-length') then + ctLen=tonumber(res:match('%d+')) or 0 + end + until res=='' + + -- Result + if code=='101' then + CHN_push(readCHN,'success') + end + + -- Content(?) + if ctLen then + res,err=SOCK:receive(ctLen) + -- print('R',res,err) + if code~='101' then + res=JSON.decode(assert(res,err)) + error((code or "XXX")..":"..(res and res.reason or "Server Error")) + end + end + + SOCK:settimeout(0) +end + +local yield=coroutine.yield +local byte,char=string.byte,string.char +local band,bor,bxor=bit.band,bit.bor,bit.bxor +local shl,shr=bit.lshift,bit.rshift + +local mask_key={1,14,5,14} +local mask_str=char(unpack(mask_key)) +local function _send(op,message) + -- Message type + SOCK:send(char(bor(op,0x80))) + + if message then + -- Length + local length=#message + if length>65535 then + SOCK:send(char(bor(127,0x80),0,0,0,0,band(shr(length,24),0xff),band(shr(length,16),0xff),band(shr(length,8),0xff),band(length,0xff))) + elseif length>125 then + SOCK:send(char(bor(126,0x80),band(shr(length,8),0xff),band(length,0xff))) + else + SOCK:send(char(bor(length,0x80))) + end + local msgbyte={byte(message,1,length)} + for i=1,length do + msgbyte[i]=bxor(msgbyte[i],mask_key[(i-1)%4+1]) + end + return SOCK:send(mask_str..char(unpack(msgbyte))) + else + SOCK:send('\128'..mask_str) + return 0 + end +end +local sendThread=coroutine.wrap(function() + while true do + while CHN_getCount(sendCHN)>=2 do + _send(CHN_pop(sendCHN),CHN_pop(sendCHN)) + end + yield() + end +end) + +local function _receive(sock,len) + local buffer="" + while true do + local r,e,p=sock:receive(len) + if r then + buffer=buffer..r + len=len-#r + elseif p then + buffer=buffer..p + len=len-#p + elseif e then + return nil,e + end + if len==0 then + return buffer + end + yield() + end +end +local readThread=coroutine.wrap(function() + local res,err + local op,fin + local lBuffer=""-- Long multi-pack buffer + while true do + -- Byte 0-1 + res,err=_receive(SOCK,2) + assert(res,err) + + op=band(byte(res,1),0x0f) + fin=band(byte(res,1),0x80)==0x80 + + -- Calculating data length + local length=band(byte(res,2),0x7f) + if length==126 then + res,err=_receive(SOCK,2) + assert(res,err) + length=shl(byte(res,1),8)+byte(res,2) + elseif length==127 then + -- 'res' is 'lenData' here + res,err=_receive(SOCK,8) + assert(res,err) + local _,_,_,_,_5,_6,_7,_8=byte(res,1,8) + length=shl(_5,24)+shl(_6,16)+shl(_7,8)+_8 + end + res,err=_receive(SOCK,length) + assert(res,err) + + -- React + if op==8 then-- 8=close + CHN_push(readCHN,8)-- close + if type(res)=='string' then + CHN_push(readCHN,res:sub(3))--[Warning] 2 bytes close code at start so :sub(3) + else + CHN_push(readCHN,"WS closed") + end + return + elseif op==0 then-- 0=continue + lBuffer=lBuffer..res + if fin then + CHN_push(readCHN,lBuffer) + -- print('M',lBuffer) + lBuffer="" + end + else + CHN_push(readCHN,op) + if fin then + CHN_push(readCHN,res) + -- print('S',res) + lBuffer="" + else + lBuffer=res + end + end + yield() + end +end) + +local success,err + +while true do-- Running + while CHN_getCount(triggerCHN)==0 do sleep(.0626) end + CHN_pop(triggerCHN) + success,err=pcall(sendThread) + if not success or err then break end + success,err=pcall(readThread) + if not success or err then break end +end + +SOCK:close() +CHN_push(readCHN,8)-- close +CHN_push(readCHN,err or "Disconnected") +error() diff --git a/Zframework/wheelScroll.lua b/Zframework/wheelScroll.lua new file mode 100644 index 000000000..d676585e8 --- /dev/null +++ b/Zframework/wheelScroll.lua @@ -0,0 +1,19 @@ +local love=love +local max,min=math.max,math.min +local trigDist=0 +return function(y,key1,key2) + if y>0 then + trigDist=max(trigDist,0)+y^1.2 + elseif y<0 then + if trigDist>0 then trigDist=0 end + trigDist=min(trigDist,0)-(-y)^1.2 + end + while trigDist>=1 do + love.keypressed(key1 or 'up') + trigDist=trigDist-1 + end + while trigDist<=-1 do + love.keypressed(key2 or 'down') + trigDist=trigDist+1 + end +end diff --git a/Zframework/widget.lua b/Zframework/widget.lua new file mode 100644 index 000000000..efbc8e188 --- /dev/null +++ b/Zframework/widget.lua @@ -0,0 +1,1546 @@ +local gc=love.graphics +local gc_origin=gc.origin +local gc_translate,gc_replaceTransform=gc.translate,gc.replaceTransform +local gc_stencil,gc_setStencilTest=gc.stencil,gc.setStencilTest +local gc_push,gc_pop=gc.push,gc.pop +local gc_setCanvas,gc_setBlendMode=gc.setCanvas,gc.setBlendMode +local gc_setColor,gc_setLineWidth=gc.setColor,gc.setLineWidth +local gc_draw,gc_line=gc.draw,gc.line +local gc_rectangle=gc.rectangle +local gc_print,gc_printf=gc.print,gc.printf + +local kb=love.keyboard +local timer=love.timer.getTime + +local next=next +local floor,ceil=math.floor,math.ceil +local max,min=math.max,math.min +local match=string.match +local sub,ins,rem=string.sub,table.insert,table.remove +local xOy=SCR.xOy +local FONT=FONT +local mStr=GC.mStr +local approach=MATH.expApproach + +local downArrowIcon=GC.DO{40,25,{'fPoly',0,0,20,25,40,0}} +local upArrowIcon=GC.DO{40,25,{'fPoly',0,25,20,0,40,25}} +local clearIcon=GC.DO{40,40, + {'fRect',16,5,8,3}, + {'fRect',8,8,24,3}, + {'fRect',11,14,18,21}, +} +local sureIcon=GC.DO{40,40, + {'rawFT',35}, + {'mText',"?",20,0}, +} +local smallerThen=GC.DO{20,20, + {'setLW',5}, + {'line',18,2,1,10,18,18}, +} +local largerThen=GC.DO{20,20, + {'setLW',5}, + {'line',2,2,19,10,2,18}, +} + +local STW,STH-- stencil-wid/hei +local function _rectangleStencil() + gc.rectangle('fill',1,1,STW-2,STH-2) +end + +local onChange=NULL + +local WIDGET={} + +function WIDGET.setOnChange(func) onChange=assert(type(func)=='function' and func,"WIDGET.setOnChange(func): func must be function") end + +local widgetMetatable={ + __tostring=function(self) + return self:getInfo() + end, +} + +local text={ + type='text', + mustHaveText=true, + alpha=0, +} + +function text:reset() end +function text:update(dt) + if self.hideF and self.hideF() then + if self.alpha>0 then + self.alpha=max(self.alpha-dt*7.5,0) + end + elseif self.alpha<1 then + self.alpha=min(self.alpha+dt*7.5,1) + end +end +function text:draw() + if self.alpha>0 then + local c=self.color + gc_setColor(c[1],c[2],c[3],self.alpha) + local w=self.obj:getWidth() + local k=min(self.lim/self.obj:getWidth(),1) + if self.align=='M' then + gc_draw(self.obj,self.x,self.y,nil,k,1,w*.5,0) + elseif self.align=='L' then + gc_draw(self.obj,self.x,self.y,nil,k,1) + elseif self.align=='R' then + gc_draw(self.obj,self.x,self.y,nil,k,1,w,0) + end + end +end +function WIDGET.newText(D)-- name,x,y[,lim][,fText][,color][,font=30][,fType][,align='M'][,hideF][,hide] + local _={ + name= D.name or "_", + x= D.x, + y= D.y, + lim= D.lim or 1e99, + + fText=D.fText, + color=D.color and (COLOR[D.color] or D.color) or COLOR.Z, + font= D.font or 30, + fType=D.fType, + align=D.align or 'M', + hideF=D.hideF, + } + for k,v in next,text do _[k]=v end + if not _.hideF then _.alpha=1 end + setmetatable(_,widgetMetatable) + return _ +end + +local image={ + type='image', +} +function image:reset() + if type(self.img)=='string' then + self.img=IMG[self.img] + end +end +function image:draw() + gc_setColor(1,1,1,self.alpha) + gc_draw(self.img,self.x,self.y,self.ang,self.k) +end +function WIDGET.newImage(D)-- name[,img(name)],x,y[,ang][,k][,hideF][,hide] + local _={ + name= D.name or "_", + img= D.img or D.name or "_", + alpha=D.alpha, + x= D.x, + y= D.y, + ang= D.ang, + k= D.k, + hideF=D.hideF, + hide= D.hide, + } + for k,v in next,image do _[k]=v end + setmetatable(_,widgetMetatable) + return _ +end + +local button={ + type='button', + mustHaveText=true, + ATV=0,-- Activating time(0~8) + textAlreadyWrapped=false,-- Text already wrapped? (Managed by :setObject, can be override, this will be true if obj has a '\n') +} +function button:reset() + self.ATV=0 +end +function button:setObject(obj) + if type(obj)=='string' or type(obj)=='number' then + if match(obj,"\n") then + self.textAlreadyWrapped=true + self.obj=gc.newText(FONT.get(self.font,self.fType)) + self.obj:addf(obj,self.w-self.edge*2,(self.align=='L' and 'left') or (self.align=='R' and 'right') or 'center') + else + self.textAlreadyWrapped=false + self.obj=gc.newText(FONT.get(self.font,self.fType),obj) + end + elseif obj then + self.obj=obj + end +end +function button:isAbove(x,y) + local ATV=self.ATV + return + x>self.x-ATV and + y>self.y and + x0 then self.ATV=max(ATV-dt*30,0) end + end +end +function button:draw() + local x,y,w,h=self.x,self.y,self.w,self.h + local ATV=self.ATV + local c=self.color + local r,g,b=c[1],c[2],c[3] + + -- Button + gc_setColor(.15+r*.7,.15+g*.7,.15+b*.7,.9) + gc_rectangle('fill',x-ATV,y,w+2*ATV,h,4) + gc_setLineWidth(2) + gc_setColor(.3+r*.7,.3+g*.7,.3+b*.7) + gc_rectangle('line',x-ATV,y,w+2*ATV,h,5) + if ATV>0 then + gc_setColor(.97,.97,.97,ATV*.125) + gc_rectangle('line',x-ATV,y,w+2*ATV,h,3) + end + + -- Drawable + local obj=self.obj + local ox,oy=obj:getWidth()*.5,obj:getHeight()*.5 + local y0=y+h*.5 + gc_setColor(1,1,1,.2+ATV*.05) + if self.align=='L' or self.textAlreadyWrapped then + local edge=self.edge + gc_draw(obj,x+edge-1,y0-1-oy) + gc_draw(obj,x+edge-1,y0+1-oy) + gc_draw(obj,x+edge+1,y0-1-oy) + gc_draw(obj,x+edge+1,y0+1-oy) + gc_setColor(r*.55,g*.55,b*.55) + gc_draw(obj,x+edge,y0-oy) + elseif self.align=='R' then + local x0=x+w-self.edge-ox*2 + gc_draw(obj,x0-1,y0-1-oy) + gc_draw(obj,x0-1,y0+1-oy) + gc_draw(obj,x0+1,y0-1-oy) + gc_draw(obj,x0+1,y0+1-oy) + gc_setColor(r*.55,g*.55,b*.55) + gc_draw(obj,x0,y0-oy) + else--if self.align=='M' then + local x0=x+w*.5 + local kx=obj:type()=='Text' and min(w/ox/2,1) or 1 + gc_draw(obj,x0-1,y0-1,nil,kx,1,ox,oy) + gc_draw(obj,x0-1,y0+1,nil,kx,1,ox,oy) + gc_draw(obj,x0+1,y0-1,nil,kx,1,ox,oy) + gc_draw(obj,x0+1,y0+1,nil,kx,1,ox,oy) + gc_setColor(r*.55,g*.55,b*.55) + gc_draw(obj,x0,y0,nil,kx,1,ox,oy) + end +end +function button:getInfo() + return("x=%d,y=%d,w=%d,h=%d,font=%d"):format(self.x+self.w*.5,self.y+self.h*.5,self.w,self.h,self.font,self.fType) +end +function button:press(_,_,k) +end +function button:drag(x,y) + if not self:isAbove(x,y) and self==WIDGET.sel then + WIDGET.unFocus() + end +end +function button:release(_,_,k) + self.code(k) + local ATV=self.ATV + SYSFX.newRectRipple( + 6, + self.x-ATV, + self.y-WIDGET.scrollPos, + self.w+2*ATV, + self.h + ) + if self.sound then + SFX.play(self.sound) + end +end +function WIDGET.newButton(D)-- name,x,y,w[,h][,fText][,color][,font=30][,fType][,sound][,align='M'][,edge=0][,code][,hideF][,hide] + if not D.h then D.h=D.w end + local w={ + name= D.name or "_", + + x= D.x-D.w*.5, + y= D.y-D.h*.5, + w= D.w, + h= D.h, + + resCtr={ + D.x,D.y, + D.x-D.w*.35,D.y-D.h*.35, + D.x-D.w*.35,D.y+D.h*.35, + D.x+D.w*.35,D.y-D.h*.35, + D.x+D.w*.35,D.y+D.h*.35, + }, + + fText=D.fText, + color=D.color and (COLOR[D.color] or D.color) or COLOR.Z, + font= D.font or 30, + fType=D.fType, + align=D.align or 'M', + edge= D.edge or 0, + code= D.code or NULL, + hideF=D.hideF, + hide= D.hide, + } + if D.sound==false then + w.sound=false + elseif type(D.sound)=='string' then + w.sound=D.sound + else + w.sound='button' + end + + for k,v in next,button do w[k]=v end + setmetatable(w,widgetMetatable) + return w +end + +local key={ + type='key', + mustHaveText=true, + ATV=0,-- Activating time(0~4) + textAlreadyWrapped=false,---See button.setObject (line 146) +} +function key:reset() + self.ATV=0 +end +function key:setObject(obj) + if type(obj)=='string' or type(obj)=='number' then + if match(obj,"\n") then + self.textAlreadyWrapped=true + self.obj=gc.newText(FONT.get(self.font,self.fType)) + self.obj:addf(obj,self.w-self.edge*2,(self.align=='L' and 'left') or (self.align=='R' and 'right') or 'center') + else + self.textAlreadyWrapped=false + self.obj=gc.newText(FONT.get(self.font,self.fType),obj) + end + elseif obj then + self.obj=obj + end +end +function key:isAbove(x,y) + return + x>self.x and + y>self.y and + x0 then self.ATV=max(ATV-dt*30,0) end + end +end +function key:draw() + local x,y,w,h=self.x,self.y,self.w,self.h + local ATV=self.ATV + local c=self.color + local align=self.align + local r,g,b=c[1],c[2],c[3] + + -- Fill + if self.fShade then + gc_setColor(r,g,b,ATV*.25) + if align=='M' then + gc_draw(self.fShade,x+w*.5-self.fShade:getWidth()*.5,y+h*.5-self.fShade:getHeight()*.5) + elseif align=='L' then + gc_draw(self.fShade,x+self.edge,y+h*.5-self.fShade:getHeight()*.5) + elseif align=='R' then + gc_draw(self.fShade,x+w-self.edge-self.fShade:getWidth(),y+h*.5-self.fShade:getHeight()*.5) + end + else + -- Background + gc_setColor(0,0,0,.3) + gc_rectangle('fill',x,y,w,h,4) + + -- Frame + gc_setColor(.2+r*.8,.2+g*.8,.2+b*.8,.7) + gc_setLineWidth(2) + gc_rectangle('line',x,y,w,h,3) + + -- Shade + gc_setColor(1,1,1,ATV*.05) + gc_rectangle('fill',x,y,w,h,3) + end + + -- Drawable + local obj=self.obj + local ox,oy=obj:getWidth()*.5,obj:getHeight()*.5 + + gc_setColor(r,g,b) + if align=='L' or self.textAlreadyWrapped then + gc_draw(obj,x+self.edge,y+h*.5-oy) + elseif align=='R' then + gc_draw(obj,x+w-self.edge-ox*2,y-oy+h*.5) + else--if align=='M' then + local kx=obj:type()=='Text' and min(w/ox/2,1) or 1 + gc_draw(obj,x+w*.5,y+h*.5,nil,kx,1,ox,oy) + end +end +function key:getInfo() + return("x=%d,y=%d,w=%d,h=%d,font=%d"):format(self.x+self.w*.5,self.y+self.h*.5,self.w,self.h,self.font,self.fType) +end +function key:press() +end +function key:drag(x,y) + if not self:isAbove(x,y) and self==WIDGET.sel then + WIDGET.unFocus() + end +end +function key:release(_,_,k) + self.code(k) + if self.sound then + SFX.play(self.sound) + end +end +function WIDGET.newKey(D)-- name,x,y,w[,h][,fText][,fShade][,color][,font=30][,fType][,sound][,align='M'][,edge=0][,code][,hideF][,hide] + if not D.h then D.h=D.w end + local _={ + name= D.name or "_", + + x= D.x-D.w*.5, + y= D.y-D.h*.5, + w= D.w, + h= D.h, + + resCtr={ + D.x,D.y, + D.x-D.w*.35,D.y-D.h*.35, + D.x-D.w*.35,D.y+D.h*.35, + D.x+D.w*.35,D.y-D.h*.35, + D.x+D.w*.35,D.y+D.h*.35, + }, + + fText= D.fText, + fShade= D.fShade, + color= D.color and (COLOR[D.color] or D.color) or COLOR.Z, + font= D.font or 30, + fType= D.fType, + align= D.align or 'M', + edge= D.edge or 0, + code= D.code or NULL, + hideF= D.hideF, + hide= D.hide, + } + if D.sound==false then + _.sound=false + elseif type(D.sound)=='string' then + _.sound=D.sound + else + _.sound='key' + end + for k,v in next,key do _[k]=v end + setmetatable(_,widgetMetatable) + return _ +end + +local switch={ + type='switch', + mustHaveText=true, + ATV=0,-- Activating time(0~8) + CHK=0,-- Check alpha(0~6) +} +function switch:reset() + self.ATV=0 + self.CHK=0 +end +function switch:isAbove(x,y) + return x>self.x and xself.y-25 and y0 then self.ATV=max(ATV-dt*30,0) end + end + local chk=self.CHK + if self:disp() then + if chk<6 then self.CHK=min(chk+dt*60,6) end + else + if chk>0 then self.CHK=max(chk-dt*60,0) end + end +end +function switch:draw() + local x,y=self.x,self.y + local ATV=self.ATV + + -- Background + gc_setColor(0,0,0,.3) + gc_rectangle('fill',x,y-25,50,50,4) + + -- Frame + gc_setLineWidth(2) + gc_setColor(1,1,1,.6+ATV*.1) + gc_rectangle('line',x,y-25,50,50,3) + + -- Checked + if ATV>0 then + gc_setColor(1,1,1,ATV*.06) + gc_rectangle('fill',x,y-25,50,50,3) + end + if self.CHK>0 then + gc_setColor(.9,1,.9,self.CHK/6) + gc_setLineWidth(5) + gc_line(x+5,y,x+18,y+13,x+45,y-14) + end + + -- Drawable + local obj=self.obj + gc_setColor(self.color) + gc_draw(obj,x-12-ATV,y,nil,min(self.lim/obj:getWidth(),1),1,obj:getWidth(),obj:getHeight()*.5) +end +function switch:getInfo() + return("x=%d,y=%d,font=%d"):format(self.x,self.y,self.font,self.fType) +end +function switch:press() + self.code() + if self.sound then + SFX.play(self.disp() and 'check' or 'uncheck') + end +end +function WIDGET.newSwitch(D)-- name,x,y[,lim][,fText][,color][,font=30][,fType][,sound=true][,disp][,code][,hideF][,hide] + local _={ + name= D.name or "_", + + x= D.x, + y= D.y, + lim= D.lim or 1e99, + + resCtr={ + D.x+25,D.y, + }, + + fText=D.fText, + color=D.color and (COLOR[D.color] or D.color) or COLOR.Z, + font= D.font or 30, + fType=D.fType, + sound=D.sound~=false, + disp= D.disp, + code= D.code or NULL, + hideF=D.hideF, + hide= D.hide, + } + for k,v in next,switch do _[k]=v end + setmetatable(_,widgetMetatable) + return _ +end + +local slider={ + type='slider', + ATV=0,-- Activating time(0~8) + TAT=0,-- Text activating time(0~180) + pos=0,-- Position shown + lastTime=0,-- Last value changing time +} +local sliderShowFunc={ + int=function(S) + return S.disp() + end, + float=function(S) + return floor(S.disp()*100+.5)*.01 + end, + percent=function(S) + return floor(S.disp()*100+.5).."%" + end, +} +function slider:reset() + self.ATV=0 + self.TAT=180 + self.pos=0 +end +function slider:isAbove(x,y) + return x>self.x-10 and xself.y-25 and y0 then + self.TAT=max(self.TAT-dt*60,0) + end + if WIDGET.sel==self then + if ATV<6 then self.ATV=min(ATV+dt*60,6) end + self.TAT=180 + else + if ATV>0 then self.ATV=max(ATV-dt*30,0) end + end + if not self.hide then + self.pos=approach(self.pos,self.disp(),dt*26) + end +end +function slider:draw() + local x,y=self.x,self.y + local ATV=self.ATV + local x2=x+self.w + + gc_setColor(1,1,1,.5+ATV*.06) + + -- Units + if not self.smooth then + gc_setLineWidth(2) + for p=self.rangeL,self.rangeR,self.unit do + local X=x+(x2-x)*(p-self.rangeL)/(self.rangeR-self.rangeL) + gc_line(X,y+7,X,y-7) + end + end + + -- Axis + gc_setLineWidth(4) + gc_line(x,y,x2,y) + + -- Block + local cx=x+(x2-x)*(self.pos-self.rangeL)/(self.rangeR-self.rangeL) + local bx,by,bw,bh=cx-10-ATV*.5,y-16-ATV,20+ATV,32+2*ATV + gc_setColor(.8,.8,.8) + gc_rectangle('fill',bx,by,bw,bh,3) + + -- Glow + if ATV>0 then + gc_setLineWidth(2) + gc_setColor(.97,.97,.97,ATV*.16) + gc_rectangle('line',bx+1,by+1,bw-2,bh-2,3) + end + + -- Float text + if self.TAT>0 and self.show then + FONT.set(25) + gc_setColor(.97,.97,.97,self.TAT/180) + mStr(self:show(),cx,by-30) + end + + -- Drawable + local obj=self.obj + if obj then + gc_setColor(self.color) + gc_draw(obj,x-12-ATV,y,nil,min(self.lim/obj:getWidth(),1),1,obj:getWidth(),obj:getHeight()*.5) + end +end +function slider:getInfo() + return("x=%d,y=%d,w=%d"):format(self.x,self.y,self.w) +end +function slider:press(x) + self:drag(x) +end +function slider:drag(x) + if not x then return end + x=x-self.x + local newPos=MATH.clamp(x/self.w,0,1) + local newVal + if not self.unit then + newVal=(1-newPos)*self.rangeL+newPos*self.rangeR + else + newVal=newPos*(self.rangeR-self.rangeL) + newVal=self.rangeL+floor(newVal/self.unit+.5)*self.unit + end + if newVal~=self.disp() then + self.code(newVal) + end + if self.change and timer()-self.lastTime>.5 then + self.lastTime=timer() + self.change() + end +end +function slider:release(x) + self:drag(x) + self.lastTime=0 +end +function slider:scroll(n) + local p=self.disp() + local u=self.unit or .01 + local P=MATH.clamp(p+u*n,self.rangeL,self.rangeR) + if p==P or not P then return end + self.code(P) + if self.change and timer()-self.lastTime>.18 then + self.lastTime=timer() + self.change() + end +end +function slider:arrowKey(k) + self:scroll((k=='left' or k=='up') and -1 or 1) +end +function WIDGET.newSlider(D)-- name,x,y,w[,lim][,fText][,color][,axis][,smooth][,font=30][,fType][,change],disp[,show][,code],hide + if not D.axis then + D.axis={0,1,false} + D.smooth=true + elseif not D.axis[3] then + D.smooth=true + end + local _={ + name= D.name or "_", + + x= D.x, + y= D.y, + w= D.w, + lim= D.lim or 1e99, + + resCtr={ + D.x,D.y, + D.x+D.w*.25,D.y, + D.x+D.w*.5,D.y, + D.x+D.w*.75,D.y, + D.x+D.w,D.y, + }, + + fText= D.fText, + color= D.color and (COLOR[D.color] or D.color) or COLOR.Z, + rangeL=D.axis[1], + rangeR=D.axis[2], + unit= D.axis[3], + smooth=D.smooth, + font= D.font or 30, + fType= D.fType, + change=D.change, + disp= D.disp, + code= D.code or NULL, + hideF= D.hideF, + hide= D.hide, + show= false, + } + if D.show then + if type(D.show)=='function' then + _.show=D.show + else + _.show=sliderShowFunc[D.show] + end + elseif D.show~=false then-- Use default if nil + if _.unit and _.unit%1==0 then + _.show=sliderShowFunc.int + else + _.show=sliderShowFunc.percent + end + end + for k,v in next,slider do _[k]=v end + setmetatable(_,widgetMetatable) + return _ +end + +local selector={ + type='selector', + mustHaveText=true, + ATV=8,-- Activating time(0~4) + select=false,-- Selected item ID + selText=false,-- Selected item name +} +function selector:reset() + self.ATV=0 + local V,L=self.disp(),self.list + for i=1,#L do + if L[i]==V then + self.select=i + self.selText=self.list[i] + return + end + end + self.select=0 + self.selText="" + MES.new('error',"Selector "..self.name.." dead, disp= "..tostring(V)) +end +function selector:isAbove(x,y) + return + x>self.x and + xself.y and + y0 then self.ATV=max(ATV-dt*30,0) end + end +end +function selector:draw() + local x,y=self.x,self.y + local w=self.w + local ATV=self.ATV + + -- Background + gc_setColor(0,0,0,.3) + gc_rectangle('fill',x,y,w,60,4) + + -- Frame + gc_setColor(1,1,1,.6+ATV*.1) + gc_setLineWidth(2) + gc_rectangle('line',x,y,w,60,3) + + -- Arrow + gc_setColor(1,1,1,.2+ATV*.1) + local t=(timer()%.5)^.5 + if self.select>1 then + gc_draw(smallerThen,x+6,y+33) + if ATV>0 then + gc_setColor(1,1,1,ATV*.4*(.5-t)) + gc_draw(smallerThen,x+6-t*40,y+33) + gc_setColor(1,1,1,.2+ATV*.1) + end + end + if self.select<#self.list then + gc_draw(largerThen,x+w-26,y+33) + if ATV>0 then + gc_setColor(1,1,1,ATV*.4*(.5-t)) + gc_draw(largerThen,x+w-26+t*40,y+33) + end + end + + -- Drawable + gc_setColor(self.color) + gc_draw(self.obj,x+w*.5,y-4,nil,min((w-20)/self.obj:getWidth(),1),1,self.obj:getWidth()*.5,0) + gc_setColor(1,1,1) + FONT.set(30) + mStr(self.selText,x+w*.5,y+22) +end +function selector:getInfo() + return("x=%d,y=%d,w=%d"):format(self.x+self.w*.5,self.y+30,self.w) +end +function selector:press(x) + if x then + local s=self.select + if x1 then + s=s-1 + SYSFX.newShade(3,self.x,self.y-WIDGET.scrollPos,self.w*.5,60) + end + else + if s<#self.list then + s=s+1 + SYSFX.newShade(3,self.x+self.w*.5,self.y-WIDGET.scrollPos,self.w*.5,60) + end + end + if self.select~=s then + self.code(self.list[s],s) + self.select=s + self.selText=self.list[s] + if self.sound then + SFX.play('selector') + end + end + end +end +function selector:scroll(n) + local s=self.select + if n==-1 then + if s==1 then return end + s=s-1 + SYSFX.newShade(3,self.x,self.y-WIDGET.scrollPos,self.w*.5,60) + else + if s==#self.list then return end + s=s+1 + SYSFX.newShade(3,self.x+self.w*.5,self.y-WIDGET.scrollPos,self.w*.5,60) + end + self.code(self.list[s]) + self.select=s + self.selText=self.list[s] + if self.sound then + SFX.play('selector') + end +end +function selector:arrowKey(k) + self:scroll((k=='left' or k=='up') and -1 or 1) +end + +function WIDGET.newSelector(D)-- name,x,y,w[,fText][,color][,sound=true],list,disp[,code],hide + local _={ + name= D.name or "_", + + x= D.x-D.w*.5, + y= D.y-30, + w= D.w, + + resCtr={ + D.x,D.y, + D.x+D.w*.25,D.y, + D.x+D.w*.5,D.y, + D.x+D.w*.75,D.y, + D.x+D.w,D.y, + }, + + fText=D.fText, + color=D.color and (COLOR[D.color] or D.color) or COLOR.Z, + sound=D.sound~=false, + font= 30, + list= D.list, + disp= D.disp, + code= D.code or NULL, + hideF=D.hideF, + hide= D.hide, + } + for k,v in next,selector do _[k]=v end + setmetatable(_,widgetMetatable) + return _ +end + +local inputBox={ + type='inputBox', + keepFocus=true, + ATV=0,-- Activating time(0~4) + value="",-- Text contained +} +function inputBox:reset() + self.ATV=0 +end +function inputBox:hasText() + return #self.value>0 +end +function inputBox:getText() + return self.value +end +function inputBox:setText(str) + if not str then str="" end + assert(type(str)=='string',"Arg #1 must be string") + self.value=str +end +function inputBox:addText(str) + if not str then str="" end + assert(type(str)=='string',"Arg #1 must be string") + self.value=self.value..str +end +function inputBox:clear() + self.value="" +end +function inputBox:isAbove(x,y) + return + x>self.x and + y>self.y and + x0 then self.ATV=max(ATV-dt*15,0) end + end +end +function inputBox:draw() + local x,y,w,h=self.x,self.y,self.w,self.h + local ATV=self.ATV + + -- Background + gc_setColor(0,0,0,.4) + gc_rectangle('fill',x,y,w,h,4) + + -- Highlight + gc_setColor(1,1,1,ATV*.08*(math.sin(timer()*4.2)*.2+.8)) + gc_rectangle('fill',x,y,w,h,4) + + -- Frame + gc_setColor(1,1,1) + gc_setLineWidth(3) + gc_rectangle('line',x,y,w,h,3) + + -- Drawable + local f=self.font + FONT.set(f,self.fType) + if self.obj then + gc_draw(self.obj,x-12-self.obj:getWidth(),y+h*.5-self.obj:getHeight()*.5) + end + if self.secret then + y=y+h*.5-f*.2 + for i=1,#self.value do + gc_rectangle("fill",x+f*.6*i,y,f*.4,f*.4) + end + else + gc_printf(self.value,x+10,y,self.w) + FONT.set(f-10) + if WIDGET.sel==self then + gc_print(EDITING,x+10,y+12-f*1.4) + end + end +end +function inputBox:getInfo() + return("x=%d,y=%d,w=%d,h=%d"):format(self.x+self.w*.5,self.y+self.h*.5,self.w,self.h) +end +function inputBox:keypress(k) + local t=self.value + if #t>0 and EDITING=="" then + if k=='backspace' then + local p=#t + while t:byte(p)>=128 and t:byte(p)<192 do + p=p-1 + end + t=sub(t,1,p-1) + SFX.play('lock') + elseif k=='delete' then + t="" + SFX.play('hold') + end + self.value=t + end +end +function WIDGET.newInputBox(D)-- name,x,y,w[,h][,font=30][,fType][,secret][,regex][,limit],hide + local _={ + name= D.name or "_", + + x= D.x, + y= D.y, + w= D.w, + h= D.h, + + resCtr={ + D.x+D.w*.2,D.y, + D.x+D.w*.5,D.y, + D.x+D.w*.8,D.y, + }, + + font= D.font or floor(D.h/7-1)*5, + fType= D.fType, + secret=D.secret==true, + regex= D.regex, + limit= D.limit, + hideF= D.hideF, + hide= D.hide, + } + for k,v in next,inputBox do _[k]=v end + setmetatable(_,widgetMetatable) + return _ +end + +local textBox={ + type='textBox', + scrollPos=0,-- Scroll-down-distance + sure=0,-- Sure-timer for clear history +} +function textBox:reset() + self.lineH=self.font*7/5 + self.capacity=ceil((self.h-10)/self.lineH) +end +function textBox:setTexts(t) + assert(type(t)=='table',"Arg #1 must be table") + TABLE.clear(self.texts) + TABLE.connect(self.texts,t) + self.scrollPos=0 +end +function textBox:clear() + self.texts={} + self.scrollPos=0 + SFX.play('fall') +end +function textBox:isAbove(x,y) + return + x>self.x and + y>self.y and + x0 then + self.sure=max(self.sure-dt,0) + end +end +function textBox:push(t) + ins(self.texts,t) + if self.scrollPos>(#self.texts-1.5)*self.lineH-self.h then-- minus 1 for the new message + self.scrollPos=max(0,min(self.scrollPos+self.lineH,#self.texts*self.lineH-self.h)) + end +end +function textBox:press(x,y) + if not (x and y) then return end + self:drag(0,0,0,0) + if not self.fix and x>self.x+self.w-40 and y0 then + self:clear() + self.sure=0 + else + self.sure=1 + end + end +end +function textBox:drag(_,_,_,dy) + self.scrollPos=max(0,min(self.scrollPos-dy,#self.texts*self.lineH-self.h)) +end +function textBox:scroll(dir) + if type(dir)=='string' then + if dir=="up" then + dir=-1 + elseif dir=="down" then + dir=1 + else + return + end + end + self:drag(nil,nil,nil,-dir*self.lineH) +end +function textBox:arrowKey(k) + if k=='up' then + self:scroll(-1) + elseif k=='down' then + self:scroll(-1) + end +end +function textBox:draw() + local x,y,w,h=self.x,self.y,self.w,self.h + local list=self.texts + local scrollPos=self.scrollPos + local lineH=self.lineH + local H=#list*lineH + + -- Background + gc_setColor(0,0,0,.3) + gc_rectangle('fill',x,y,w,h,4) + + -- Frame + gc_setLineWidth(2) + gc_setColor(WIDGET.sel==self and COLOR.lN or COLOR.Z) + gc_rectangle('line',x,y,w,h,3) + + -- Texts + FONT.set(self.font,self.fType) + gc_push('transform') + gc_translate(x,y) + + -- Slider + gc_setColor(1,1,1) + if #list>self.capacity then + local len=h*h/H + gc_rectangle('fill',-15,(h-len)*scrollPos/(H-h),12,len,3) + end + + -- Clear button + if not self.fix then + gc_rectangle('line',w-40,0,40,40,3) + gc_draw(self.sure==0 and clearIcon or sureIcon,w-40,0) + end + + gc_setStencilTest('equal',1) + STW,STH=w,h + gc_stencil(_rectangleStencil) + gc_translate(0,-(scrollPos%lineH)) + local pos=floor(scrollPos/lineH) + for i=pos+1,min(pos+self.capacity+1,#list) do + if list[i]~=nil then + gc_printf(list[i],10,4,w-16) + end + gc_translate(0,lineH) + end + gc_setStencilTest() + gc_pop() +end +function textBox:getInfo() + return("x=%d,y=%d,w=%d,h=%d"):format(self.x+self.w*.5,self.y+self.h*.5,self.w,self.h) +end +function WIDGET.newTextBox(D)-- name,x,y,w,h[,font=30][,fType][,lineH][,fix],hide + local _={ + name= D.name or "_", + + resCtr={ + D.x+D.w*.5,D.y+D.h*.5, + D.x+D.w*.5,D.y, + D.x-D.w*.5,D.y, + D.x,D.y+D.h*.5, + D.x,D.y-D.h*.5, + D.x,D.y, + D.x+D.w,D.y, + D.x,D.y+D.h, + D.x+D.w,D.y+D.h, + }, + + x= D.x, + y= D.y, + w= D.w, + h= D.h, + + font= D.font or 30, + lineH=D.lineH, + capacity=nil, + fType=D.fType, + fix= D.fix, + texts={}, + hideF=D.hideF, + hide= D.hide, + } + + for k,v in next,textBox do _[k]=v end + setmetatable(_,widgetMetatable) + return _ +end + +local listBox={ + type='listBox', + keepFocus=true, + scrollPos=0,-- Scroll-down-distance + selected=0,-- Hidden wheel move value + _pressX=false, + _pressY=false, +} +function listBox:reset() + -- haha nothing here too, techmino is really fun! +end +function listBox:clear() + self.list={} + self.scrollPos=0 +end +function listBox:setList(t) + assert(type(t)=='table',"Arg #1 must be table") + self.list=t + self.selected=1 + self.scrollPos=0 +end +function listBox:getList() + return self.list +end +function listBox:getLen() + return #self.list +end +function listBox:getSel() + return self.list[self.selected] +end +function listBox:isAbove(x,y) + return + x>self.x and + y>self.y and + x0 then + rem(self.list) + listBox:drag(0,0,0,0) + end +end +function listBox:remove() + if self.selected then + rem(self.list,self.selected) + if not self.list[self.selected] then + self:arrowKey('up') + end + self:drag(0,0,0,0) + end +end +function listBox:press(x,y) + self._pressX=x + self._pressY=y +end +function listBox:release(x,y) + if not (x and y) then return end + if self._pressX then + self._pressX,self._pressY=false,false + x,y=x-self.x,y-self.y + if not (x and y and x>0 and y>0 and x<=self.w and y<=self.h) then return end + y=math.floor((y+self.scrollPos)/self.lineH)+1 + if self.list[y] then + if self.selected~=y then + self.selected=y + self:drag(0,0,0,0) + SFX.play('selector',.8,0,12) + end + end + end +end +function listBox:drag(x,y,_,dy) + if x and y and self._pressX and MATH.distance(x,y,self._pressX,self._pressY)>10 then + self._pressX,self._pressY=false,false + end + self.scrollPos=max(0,min(self.scrollPos-dy,#self.list*self.lineH-self.h)) +end +function listBox:scroll(n) + self:drag(nil,nil,nil,-n*self.lineH) +end +function listBox:arrowKey(dir) + if dir=="up" then + self.selected=max(self.selected-1,1) + if self.selectedfloor(self.scrollPos/self.lineH)+self.capacity-1 then + self:drag(nil,nil,nil,-self.lineH) + end + end +end +function listBox:select(i) + self.selected=i + if self.selectedfloor(self.scrollPos/self.lineH)+self.capacity-1 then + self:drag(nil,nil,nil,-1e99) + end +end +function listBox:draw() + local x,y,w,h=self.x,self.y,self.w,self.h + local list=self.list + local scrollPos=self.scrollPos + local lineH=self.lineH + local H=#list*lineH + + gc_push('transform') + gc_translate(x,y) + + -- Background + gc_setColor(0,0,0,.4) + gc_rectangle('fill',0,0,w,h,4) + + -- Frame + gc_setColor(WIDGET.sel==self and COLOR.lN or COLOR.Z) + gc_setLineWidth(2) + gc_rectangle('line',0,0,w,h,3) + + -- Slider + if #list>self.capacity then + gc_setColor(1,1,1) + local len=h*h/H + gc_rectangle('fill',-15,(h-len)*scrollPos/(H-h),12,len,3) + end + + -- List + gc_setStencilTest('equal',1) + STW,STH=w,h + gc_stencil(_rectangleStencil) + local pos=floor(scrollPos/lineH) + gc_translate(0,-(scrollPos%lineH)) + for i=pos+1,min(pos+self.capacity+1,#list) do + if list[i]~=nil then + self.drawF(list[i],i,i==self.selected) + end + gc_translate(0,lineH) + end + gc_setStencilTest() + gc_pop() +end +function listBox:getInfo() + return("x=%d,y=%d,w=%d,h=%d"):format(self.x+self.w*.5,self.y+self.h*.5,self.w,self.h) +end +function WIDGET.newListBox(D)-- name,x,y,w,h,lineH,drawF[,hideF][,hide] + local _={ + name= D.name or "_", + + resCtr={ + D.x+D.w*.5,D.y+D.h*.5, + D.x+D.w*.5,D.y, + D.x-D.w*.5,D.y, + D.x,D.y+D.h*.5, + D.x,D.y-D.h*.5, + D.x,D.y, + D.x+D.w,D.y, + D.x,D.y+D.h, + D.x+D.w,D.y+D.h, + }, + + x= D.x, + y= D.y, + w= D.w, + h= D.h, + + list= {}, + lineH= D.lineH, + capacity=ceil(D.h/D.lineH), + drawF= D.drawF, + hideF= D.hideF, + hide= D.hide, + } + + for k,v in next,listBox do _[k]=v end + setmetatable(_,widgetMetatable) + return _ +end + +WIDGET.active={}-- Table contains all active widgets +WIDGET.scrollHeight=0-- Max drag height, not actual container height! +WIDGET.scrollPos=0-- Current scroll position +WIDGET.sel=false-- Selected widget +WIDGET.indexMeta={ + __index=function(L,k) + for i=1,#L do + if L[i].name==k then + return L[i] + end + end + end +} +function WIDGET.setWidgetList(list) + WIDGET.unFocus(true) + WIDGET.active=list or NONE + WIDGET.cursorMove(xOy:inverseTransformPoint(love.mouse.getPosition())) + + -- Reset all widgets + if list then + for i=1,#list do + list[i]:reset() + end + onChange() + end +end +function WIDGET.setScrollHeight(height) + WIDGET.scrollHeight=height and height or 0 + WIDGET.scrollPos=0 +end +function WIDGET.setLang(widgetText) + for S,L in next,SCN.scenes do + if L.widgetList then + for _,W in next,L.widgetList do + local t=W.fText or widgetText[S][W.name] + if not t and W.mustHaveText then + t=W.name or "##" + W.color=COLOR.dV + end + if type(W.setObject)=='function' then + W:setObject(t) + elseif type(t)=='string' and W.font then + W.obj=gc.newText(FONT.get(W.font or 30),t) + else + W.obj=t + end + end + end + end +end +function WIDGET.getSelected() + return WIDGET.sel +end +function WIDGET.isFocus(W) + if W then + return W and WIDGET.sel==W + else + return WIDGET.sel~=false + end +end +function WIDGET.focus(W) + if WIDGET.sel==W or (W and W.hide) then return end + if WIDGET.sel and WIDGET.sel.type=='inputBox' then + kb.setTextInput(false) + EDITING="" + end + WIDGET.sel=W + if W and W.type=='inputBox' then + local _,y1=xOy:transformPoint(0,W.y+W.h) + kb.setTextInput(true,0,y1,1,1) + end +end +function WIDGET.unFocus(force) + local W=WIDGET.sel + if W and (force or not W.keepFocus) then + if W.type=='inputBox' then + kb.setTextInput(false) + EDITING="" + end + WIDGET.sel=false + end +end + +function WIDGET.cursorMove(x,y) + for _,W in next,WIDGET.active do + if not W.hide and W.resCtr and W:isAbove(x,y+WIDGET.scrollPos) then + WIDGET.focus(W) + return + end + end + if WIDGET.sel and not WIDGET.sel.keepFocus then + WIDGET.unFocus() + end +end +function WIDGET.press(x,y,k) + local W=WIDGET.sel + if W then + if W.press then + W:press(x,y and y+WIDGET.scrollPos,k) + end + if W.hide then WIDGET.unFocus() end + end +end +function WIDGET.drag(x,y,dx,dy) + if WIDGET.sel then + local W=WIDGET.sel + if W.drag then + W:drag(x,y+WIDGET.scrollPos,dx,dy) + end + else + WIDGET.scrollPos=max(min(WIDGET.scrollPos-dy,WIDGET.scrollHeight),0) + end +end +function WIDGET.release(x,y,k) + local W=WIDGET.sel + if W and W.release then + W:release(x,y+WIDGET.scrollPos,k) + end +end +function WIDGET.textinput(texts) + local W=WIDGET.sel + if W and W.type=='inputBox' then + if (not W.regex or texts:match(W.regex)) and (not W.limit or #(WIDGET.sel.value..texts)<=W.limit) then + WIDGET.sel.value=WIDGET.sel.value..texts + SFX.play('touch') + else + SFX.play('drop_cancel') + end + end +end + +function WIDGET.update(dt) + for _,W in next,WIDGET.active do + if W.hideF then + W.hide=W.hideF() + if W.hide and W==WIDGET.sel then + WIDGET.unFocus(true) + end + end + if W.update then W:update(dt) end + end +end +local widgetCanvas +local widgetCover do + local L={1,360,{'fRect',0,30,1,300}} + for i=0,30 do + ins(L,{'setCL',1,1,1,i/30}) + ins(L,{'fRect',0,i,1,2}) + ins(L,{'fRect',0,360-i,1,2}) + end + widgetCover=GC.DO(L) +end +local scr_w,scr_h +function WIDGET.resize(w,h) + scr_w,scr_h=w,h + widgetCanvas=gc.newCanvas(w,h) +end +function WIDGET.draw() + gc_replaceTransform(xOy) + gc_setCanvas({stencil=true},widgetCanvas) + gc_translate(0,-WIDGET.scrollPos) + for _,W in next,WIDGET.active do + if not W.hide then W:draw() end + end + gc_origin() + gc_setColor(1,1,1) + if WIDGET.scrollHeight>0 then + if WIDGET.scrollPos>0 then + gc_draw(upArrowIcon,scr_w*.5,10,0,SCR.k,nil,upArrowIcon:getWidth()*.5,0) + end + if WIDGET.scrollPos>'..k..string.rep(" ",26-#k),debug.traceback():match("\n.-\n\t(.-): "))rawset(self,k,v) end}) ---Var leak check --- setmetatable(_G,{__newindex=function(self,k,v)print('>>'..k)print(debug.traceback():match("\n.-\n\t(.-): "))rawset(self,k,v)end}) - ---System Global Vars Declaration +-- System Global Vars Declaration local fs=love.filesystem VERSION=require"version" TIME=love.timer.getTime -YIELD=coroutine.yield -SYSTEM=love.system.getOS()if SYSTEM=='OS X'then SYSTEM='macOS'end -FNNS=SYSTEM:find'\79\83'--What does FNSF stand for? IDK so don't ask me lol -MOBILE=SYSTEM=='Android'or SYSTEM=='iOS' -SAVEDIR=fs.getSaveDirectory() ---Global Vars & Settings +-- Global Vars & Settings SFXPACKS={'chiptune'} -VOCPACKS={'miya','mono','xiaoya','miku'} +VOCPACKS={'miya','mono','xiaoya','flore','neuro','miku','zundamon'} FIRSTLAUNCH=false DAILYLAUNCH=false ---System setting +-- System setting math.randomseed(os.time()*626) love.setDeprecationOutput(false) love.keyboard.setKeyRepeat(true) love.keyboard.setTextInput(false) -if MOBILE then - local w,h,f=love.window.getMode() - f.resizable=false - love.window.setMode(w,h,f) -end local _LOADTIMELIST_={} local _LOADTIME_=TIME() ---Load modules +-- Load modules Z=require'Zframework' FONT.load{ norm='parts/fonts/proportional.otf', @@ -57,89 +45,108 @@ FONT.load{ FONT.setDefault('norm') FONT.setFallback('norm') -SCR.setSize(1280,720)--Initialize Screen size +SCR.setSize(1280,720) -- Initialize Screen size BGM.setMaxSources(5) -BGM.setChange(function(name)MES.new('music',text.nowPlaying..name,5)end) VOC.setDiversion(.62) +if SYSTEM == 'Web' and not WEB_COMPAT_MODE then + CLIPBOARD.setFreshInterval(.5) +end + WIDGET.setOnChange(function() - if SCN.cur~='custom_field'then - local colorList=THEME.getThemeColor() - if not colorList then return end - local rnd=math.random + if SCN.cur=='net_game' or SCN.cur=='custom_field' then return end + local colorList=THEME.getThemeColor() + if colorList then for _,W in next,SCN.scenes[SCN.cur].widgetList do - if W.color then - W.color=colorList[rnd(#colorList)] - end + W.color=W.color and colorList[math.random(#colorList)] end end end) -table.insert(_LOADTIMELIST_,("Load Zframework: %.3fs"):format(TIME()-_LOADTIME_)) - ---Create shortcuts +-- Create shortcuts setFont=FONT.set getFont=FONT.get -mStr=GC.mStr mText=GC.simpX -mDraw=GC.draw +mDraw=GC.mDraw Snd=SFX.playSample string.repD=STRING.repD string.sArg=STRING.sArg string.split=STRING.split ---Delete all naked files (from too old version) +-- Delete all naked files (from ancient versions) FILE.clear('') ---Create directories -for _,v in next,{'conf','record','replay','cache','lib'}do +-- Create directories +for _,v in next,{'conf','record','replay','cache','lib'} do local info=fs.getInfo(v) if not info then fs.createDirectory(v) - elseif info.type~='directory'then + elseif info.type~='directory' then fs.remove(v) fs.createDirectory(v) end end +-- Load shader files from SOURCE ONLY +SHADER={} +for _,v in next,fs.getDirectoryItems('parts/shaders') do + if FILE.isSafe('parts/shaders/'..v) then + local name=v:sub(1,-6) + SHADER[name]=love.graphics.newShader('parts/shaders/'..name..'.glsl') + end +end + +-- Load modules CHAR=require'parts.char' require'parts.gameTables' require'parts.gameFuncs' ---Load shader files from SOURCE ONLY -SHADER={} -for _,v in next,fs.getDirectoryItems('parts/shaders')do - if isSafeFile('parts/shaders/'..v)then - local name=v:sub(1,-6) - SHADER[name]=love.graphics.newShader('parts/shaders/'..name..'.glsl') +THEME =require'parts.theme' +LINE =require'parts.line' +DATA =require'parts.data' + +TEXTURE=require'parts.texture' +SKIN =require'parts.skin' +USERS =require'parts.users' +NET =require'parts.net' +VK =require'parts.virtualKey' +BOT =require'parts.bot' +RSlist =require'parts.RSlist'; DSCP=RSlist.TRS.centerPos +PLY =require'parts.player' +NETPLY =require'parts.netPlayer' +MODES =require'parts.modes' + +setmetatable(TEXTURE,{ + __index=function(self,k) + MES.new('warn',"No texture called: "..k) + self[k]=PAPER + return self[k] + end, +}) + +-- Load mode files +for i=1,#MODES do + local m=MODES[i] -- Mode template + if FILE.isSafe('parts/modes/'..m.name) then + TABLE.complete(require('parts.modes.'..m.name),MODES[i]) + MODES[m.name],MODES[i]=MODES[i] + end +end +for _,v in next,fs.getDirectoryItems('parts/modes') do + if FILE.isSafe('parts/modes/'..v) and not MODES[v:sub(1,-5)] then + local M={name=v:sub(1,-5)} + local modeData=require('parts.modes.'..M.name) + if modeData.env then + TABLE.complete(modeData,M) + MODES[M.name]=M + end end end -THEME= require'parts.theme' -LINE= require'parts.line' -DATA= require'parts.data' - -TEXTURE= require'parts.texture' -SKIN= require'parts.skin' -USERS= require'parts.users' -NET= require'parts.net' -VK= require'parts.virtualKey' -BOT= require'parts.bot' -RSlist= require'parts.RSlist'DSCP=RSlist.TRS.centerPos -PLY= require'parts.player' -NETPLY= require'parts.netPlayer' -MODES= require'parts.modes' - -setmetatable(TEXTURE,{__index=function(self,k) - MES.new('warn',"No texture called: "..k) - self[k]=PAPER - return self[k] -end}) - -table.insert(_LOADTIMELIST_,("Load Parts: %.3fs"):format(TIME()-_LOADTIME_)) - ---Init Zframework -do--Z.setCursor +table.insert(_LOADTIMELIST_,("Load Modules: %.3fs"):format(TIME()-_LOADTIME_)) + +-- Initialize Zframework +do -- Z.setCursor local normImg=GC.DO{16,16, {'fCirc',8,8,4}, {'setCL',1,1,1,.7}, @@ -150,28 +157,26 @@ do--Z.setCursor {'dCirc',8,8,7}, {'fCirc',8,8,3}, } - local min,int,abs=math.min,math.floor,math.abs - local gc_setColor,gc_draw=love.graphics.setColor,love.graphics.draw - local ms=love.mouse + local _ Z.setCursor(function(time,x,y) if not SETTING.sysCursor then - local R=int((time+1)/2)%7+1 + local R=math.floor((time+1)/2)%7+1 _=BLOCK_COLORS[SETTING.skin[R]] - gc_setColor(_[1],_[2],_[3],min(abs(1-time%2),.3)) + GC.setColor(_[1],_[2],_[3],math.min(math.abs(1-time%2),.3)) _=DSCP[R][0] - gc_draw(TEXTURE.miniBlock[R],x,y,time%3.14159265359*4,8,8,2*_[2]+1,2*(#BLOCKS[R][0]-_[1])-1) - gc_setColor(1,1,1) - gc_draw(ms.isDown(1)and holdImg or normImg,x,y,nil,nil,nil,8,8) + GC.draw(TEXTURE.miniBlock[R],x,y,time%math.pi*4,8,8,2*_[2]+1,2*(#BLOCKS[R][0]-_[1])-1) + GC.setColor(1,1,1) + GC.draw(love.mouse.isDown(1) and holdImg or normImg,x,y,nil,nil,nil,8,8) end end) end Z.setOnFnKeys({ - function()MES.new('check',PROFILE.switch()and"profile start!"or"profile report copied!")end, - function()MES.new('info',("System:%s[%s]\nluaVer:%s\njitVer:%s\njitVerNum:%s"):format(SYSTEM,jit.arch,_VERSION,jit.version,jit.version_num))end, - function()MES.new('error',"挂了")end, + function() MES.new('check',PROFILE.switch() and "profile start!" or "profile report copied!") end, + function() MES.new('info',("System:%s[%s]\nluaVer:%s\njitVer:%s\njitVerNum:%s"):format(SYSTEM,jit.arch,_VERSION,jit.version,jit.version_num)) end, + function() MES.new('error',"挂了") end, function() if GAME.playing and not GAME.net then - for _=1,8 do + for _=1,1 do if #PLY_ALIVE>1 then local P=PLY_ALIVE[math.random(2,#PLY_ALIVE)] P.lastRecv=PLAYERS[1] @@ -180,17 +185,25 @@ Z.setOnFnKeys({ end end end, - function()print(BG.locked)end, - function()for k,v in next,_G do print(k,v)end end, - function()if love['_openConsole']then love['_openConsole']()end end, + function() print(BG.locked) end, + function() for k,v in next,_G do print(k,v) end end, + function() if love['_openConsole'] then love['_openConsole']() end end, }) +Z.setOnGlobalKey('f11',function() + if not MOBILE then + SETTING.fullscreen=not SETTING.fullscreen + applySettings('fullscreen') + saveSettings() + end +end) +Z.setVersionText(VERSION.string) Z.setDebugInfo{ - {"Cache",gcinfo}, - {"Tasks",TASK.getCount}, + {"Cache", gcinfo}, + {"Tasks", TASK.getCount}, {"Voices",VOC.getQueueCount}, - {"Audios",love.audio.getSourceCount}, + {"Audios",(love.audio and (love.audio.getActiveSourceCount or love.audio.getSourceCount)) or function() return 0 end}, } -do--Z.setOnFocus +do -- Z.setOnFocus local function task_autoSoundOff() while true do coroutine.yield() @@ -218,7 +231,7 @@ do--Z.setOnFocus TASK.new(task_autoSoundOn) end else - if SCN.cur=='game'and SETTING.autoPause then + if SCN.cur=='game' and SETTING.autoPause then pauseGame() end if SETTING.autoMute and SCN.cur~='music' then @@ -228,48 +241,18 @@ do--Z.setOnFocus end end) end -Z.setOnQuit(destroyPlayers) - ---Load settings and statistics -if - not( - pcall(TABLE.cover, loadFile('conf/user', '-json -canSkip')or loadFile('conf/user', '-luaon -canSkip')or{},USER) and - pcall(TABLE.cover, loadFile('conf/unlock', '-json -canSkip')or loadFile('conf/unlock', '-luaon -canSkip')or{},RANKS) and - pcall(TABLE.update,loadFile('conf/settings', '-json -canSkip')or loadFile('conf/settings', '-luaon -canSkip')or{},SETTING) and - pcall(TABLE.coverR,loadFile('conf/data', '-json -canSkip')or loadFile('conf/data', '-luaon -canSkip')or{},STAT) and - pcall(TABLE.cover, loadFile('conf/key', '-json -canSkip')or loadFile('conf/key', '-luaon -canSkip')or{},KEY_MAP) and - pcall(TABLE.cover, loadFile('conf/virtualkey','-json -canSkip')or loadFile('conf/virtualkey','-luaon -canSkip')or{},VK_ORG) - ) -then - MES.new('error',"Be careful, an error accured when loading saving, some data was lost") -end - ---Initialize fields, sequence, missions, gameEnv for cutsom game -local fieldData=loadFile('conf/customBoards','-string -canSkip') -if fieldData then - fieldData=STRING.split(fieldData,"!") - for i=1,#fieldData do - DATA.pasteBoard(fieldData[i],i) - end -else - FIELD[1]=DATA.newBoard() -end -local sequenceData=loadFile('conf/customSequence','-string -canSkip') -if sequenceData then - DATA.pasteSequence(sequenceData) -end -local missionData=loadFile('conf/customMissions','-string -canSkip') -if missionData then - DATA.pasteMission(missionData) -end -local customData=loadFile('conf/customEnv','-canSkip') -if customData and customData['version']==VERSION.code then - TABLE.complete(customData,CUSTOMENV) -end -TABLE.complete(require"parts.customEnv0",CUSTOMENV) - +Z.setOnBeforeQuit(function() + NET.ws_close() + TASK.new(function() + TEST.yieldT(.26) + love.event.quit() + end) +end) +Z.setOnQuit(function() + destroyPlayers() +end) ---Initialize image libs +-- Initialize image libs IMG.init{ lock='media/image/mess/lock.png', dialCircle='media/image/mess/dialCircle.png', @@ -277,7 +260,6 @@ IMG.init{ lifeIcon='media/image/mess/life.png', badgeIcon='media/image/mess/badge.png', ctrlSpeedLimit='media/image/mess/ctrlSpeedLimit.png', - speedLimit='media/image/mess/speedLimit.png',--Not used, for future C2-mode pay1='media/image/mess/pay1.png', pay2='media/image/mess/pay2.png', @@ -290,7 +272,10 @@ IMG.init{ monoCH='media/image/characters/mono.png', xiaoyaCH='media/image/characters/xiaoya.png', xiaoyaOmino='media/image/characters/xiaoya_Omino.png', + floreCH='media/image/characters/flore.png', mikuCH='media/image/characters/miku.png', + zundamonCH='media/image/characters/zundamon.png', + neuroCH='media/image/characters/neuro.png', z={ character='media/image/characters/z_character.png', screen1='media/image/characters/z_screen1.png', @@ -313,55 +298,57 @@ IMG.init{ }, } SKIN.load{ - {name="crystal_scf",path='media/image/skin/crystal_scf.png'}, - {name="matte_mrz",path='media/image/skin/matte_mrz.png'}, - {name="shiny_chno",path='media/image/skin/shiny_chno.png'}, - {name="contrast_mrz",path='media/image/skin/contrast_mrz.png'}, - {name="polkadots_scf",path='media/image/skin/polkadots_scf.png'}, - {name="toy_scf",path='media/image/skin/toy_scf.png'}, - {name="smooth_mrz",path='media/image/skin/smooth_mrz.png'}, - {name="simple_scf",path='media/image/skin/simple_scf.png'}, - {name="glass_scf",path='media/image/skin/glass_scf.png'}, - {name="penta_scf",path='media/image/skin/penta_scf.png'}, - {name="bubble_scf",path='media/image/skin/bubble_scf.png'}, - {name="minoes_scf",path='media/image/skin/minoes_scf.png'}, - {name="pure_mrz",path='media/image/skin/pure_mrz.png'}, - {name="bright_scf",path='media/image/skin/bright_scf.png'}, - {name="glow_mrz",path='media/image/skin/glow_mrz.png'}, - {name="plastic_mrz",path='media/image/skin/plastic_mrz.png'}, - {name="paper_mrz",path='media/image/skin/paper_mrz.png'}, - {name="yinyang_scf",path='media/image/skin/yinyang_scf.png'}, - {name="cartooncup_earety",path='media/image/skin/cartooncup_earety.png'}, - {name="jelly_miya",path='media/image/skin/jelly_miya.png'}, - {name="guidetris_xmiao_lusisi",path='media/image/skin/guidetris_xmiao_lusisi.png'}, - {name="brick_notypey",path='media/image/skin/brick_notypey.png'}, - {name="gem_notypey",path='media/image/skin/gem_notypey.png'}, - {name="classic",path='media/image/skin/classic_unknown.png'}, - {name="ball_shaw",path='media/image/skin/ball_shaw.png'}, - {name="retro_notypey",path='media/image/skin/retro_notypey.png'}, - {name="pixel_chno",path='media/image/skin/pixel_chno.png'}, - {name="pastel_chno",path='media/image/skin/pastel_chno.png'}, - {name="letters_chno",path='media/image/skin/letters_chno.png'}, - {name="kanji_chno",path='media/image/skin/kanji_chno.png'}, - {name="textbone_mrz",path='media/image/skin/textbone_mrz.png'}, - {name="coloredbone_mrz",path='media/image/skin/coloredbone_mrz.png'}, - {name="wtf",path='media/image/skin/wtf_mrz.png'}, + {name="Crystal (Scf)", path='media/image/skin/scf/crystal.png'}, + {name="Smooth (MrZ)", path='media/image/skin/mrz/smooth.png'}, + {name="Matte (MrZ)", path='media/image/skin/mrz/matte.png'}, + {name="Glass (Scf)", path='media/image/skin/scf/glass.png'}, + {name="Jelly (Miya)", path='media/image/skin/miya/jelly.png'}, + {name="Simple (Scf)", path='media/image/skin/scf/simple.png'}, + {name="Contrast (MrZ)", path='media/image/skin/mrz/contrast.png'}, + {name="Plastic (MrZ)", path='media/image/skin/mrz/plastic.png'}, + {name="Glow (MrZ)", path='media/image/skin/mrz/glow.png'}, + {name="Bright (Scf)", path='media/image/skin/scf/bright.png'}, + {name="Penta (Scf)", path='media/image/skin/scf/penta.png'}, + {name="Bubble (Scf)", path='media/image/skin/scf/bubble.png'}, + {name="Pure (MrZ)", path='media/image/skin/mrz/pure.png'}, + {name="Letters (CHNO)", path='media/image/skin/chno/letters.png'}, + {name="Kanji (CHNO)", path='media/image/skin/chno/kanji.png'}, + {name="Pastel (CHNO)", path='media/image/skin/chno/pastel.png'}, + {name="Classic", path='media/image/skin/unknown/classic.png'}, + {name="Arcade (Asriel)", path='media/image/skin/asriel/arcade.png'}, + {name="Shiny (CHNO)", path='media/image/skin/chno/shiny.png'}, + {name="Brick (Notypey)", path='media/image/skin/notypey/brick.png'}, + {name="Cartooncup (Earety)", path='media/image/skin/earety/cartooncup.png'}, + {name="Paper (MrZ)", path='media/image/skin/mrz/paper.png'}, + {name="Toy (Scf)", path='media/image/skin/scf/toy.png'}, + {name="Polkadots (Scf)", path='media/image/skin/scf/polkadots.png'}, + {name="Yinyang (Scf)", path='media/image/skin/scf/yinyang.png'}, + {name="Minoes (Scf)", path='media/image/skin/scf/minoes.png'}, + {name="Cardboard (Asriel, slimenergy)", path='media/image/skin/asriel/cardboard.png'}, + {name="Ball (Shaw)", path='media/image/skin/shaw/ball.png'}, + {name="Gem (Notypey)", path='media/image/skin/notypey/gem.png'}, + {name="Pixel (CHNO)", path='media/image/skin/chno/pixel.png'}, + {name="Retro (Notypey)", path='media/image/skin/notypey/retro.png'}, + {name="Guidetris (xmiao, lusisi)", path='media/image/skin/guidetris_xmiao_lusisi.png'}, + {name="Textbone (MrZ)", path='media/image/skin/mrz/textbone.png'}, + {name="Coloredbone (MrZ)", path='media/image/skin/mrz/coloredbone.png'}, + {name="WTF (MrZ)", path='media/image/skin/mrz/wtf.png'}, } ---Initialize sound libs -SFX.init((function()--[Warning] Not loading files here, just get the list of sound needed +-- Initialize sound libs +SFX.init((function() --[Warning] Not loading files here, just get the list of sound needed local L={} - for _,v in next,fs.getDirectoryItems('media/effect/chiptune/')do - if isSafeFile('media/effect/chiptune/'..v,"Dangerous file : %SAVE%/media/effect/chiptune/"..v)then + for _,v in next,fs.getDirectoryItems('media/effect/chiptune/') do + if FILE.isSafe('media/effect/chiptune/'..v) then table.insert(L,v:sub(1,-5)) end end return L end)()) -BGM.load((function() +BGM.init((function() local L={} - for _,v in next,fs.getDirectoryItems('media/music')do - if isSafeFile('media/music/'..v,"Dangerous file : %SAVE%/media/music/"..v)then + for _,v in next,fs.getDirectoryItems('media/music') do + if FILE.isSafe('media/music/'..v) then L[v:sub(1,-5)]='media/music/'..v end end @@ -369,7 +356,7 @@ BGM.load((function() end)()) VOC.init{ 'zspin','sspin','jspin','lspin','tspin','ospin','ispin','pspin','qspin','fspin','espin','uspin','vspin','wspin','xspin','rspin','yspin','nspin','hspin','cspin', - 'single','double','triple','techrash','pentacrash','hexacrash', + 'single','double','triple','techrash','pentacrash','hexacrash','heptacrash','octacrash','nonacrash','decacrash','undecacrash','dodecacrash','tridecacrash','tetradecacrash','pentadecacrash','hexadecacrash','heptadecacrash','octadecacrash','nonadecacrash','ultracrash','impossicrash', 'mini','b2b','b3b', 'perfect_clear','half_clear', 'win','lose','bye', @@ -377,232 +364,183 @@ VOC.init{ 'welcome', } ---Initialize language lib -LANG.init('zh', - { - zh=require'parts.language.lang_zh', - zh_trad=require'parts.language.lang_zh_trad', - en=require'parts.language.lang_en', - fr=require'parts.language.lang_fr', - es=require'parts.language.lang_es', - pt=require'parts.language.lang_pt', - id=require'parts.language.lang_id', - ja=require'parts.language.lang_ja', - symbol=require'parts.language.lang_symbol', - zh_code=require'parts.language.lang_zh_code', - --1. Add language file to LANG folder; - --2. Require it; - --3. Add a button in parts/scenes/lang.lua; - }, - { - block=BLOCK_NAMES - }, - (function() - local tipMeta={__call=function(L)return L[math.random(#L)]end} - return function(L) - if type(rawget(L,'getTip'))=='table'then setmetatable(L.getTip,tipMeta)end - setmetatable(L,{__index=function(self,k) - local mes="No Text ("..SETTING.locale.."): "..k - LOG(mes) - MES.new('warn',mes) - self[k]=CHAR.zChan.thinking - return self[k] - end}) - end - end)() -) +table.insert(_LOADTIMELIST_,("Initialize Modules: %.3fs"):format(TIME()-_LOADTIME_)) -table.insert(_LOADTIMELIST_,("Initialize Parts: %.3fs"):format(TIME()-_LOADTIME_)) - ---Load background files from SOURCE ONLY -for _,v in next,fs.getDirectoryItems('parts/backgrounds')do - if isSafeFile('parts/backgrounds/'..v)and v:sub(-3)=='lua'then - local name=v:sub(1,-5) - BG.add(name,require('parts.backgrounds.'..name)) - end -end -BG.remList('none')BG.remList('gray')BG.remList('custom') ---Load scene files from SOURCE ONLY -for _,v in next,fs.getDirectoryItems('parts/scenes')do - if isSafeFile('parts/scenes/'..v)then - local sceneName=v:sub(1,-5) - SCN.add(sceneName,require('parts.scenes.'..sceneName)) - LANG.addScene(sceneName) - end -end ---Load mode files -for i=1,#MODES do - local m=MODES[i]--Mode template - if isSafeFile('parts/modes/'..m.name)then - TABLE.complete(require('parts.modes.'..m.name),MODES[i]) - MODES[m.name],MODES[i]=MODES[i] - end -end -for _,v in next,fs.getDirectoryItems('parts/modes')do - if isSafeFile('parts/modes/'..v)and not MODES[v:sub(1,-5)]then - local M={name=v:sub(1,-5)} - local modeData=require('parts.modes.'..M.name) - if modeData.env then - TABLE.complete(modeData,M) - MODES[M.name]=M - end - end +-- Load settings and statistics +if + not ( + pcall(TABLE.cover, loadFile('conf/user', '-json -canSkip') or loadFile('conf/user', '-luaon -canSkip') or {},USER) and + pcall(TABLE.cover, loadFile('conf/unlock', '-json -canSkip') or loadFile('conf/unlock', '-luaon -canSkip') or {},RANKS) and + pcall(TABLE.update,loadFile('conf/settings', '-json -canSkip') or loadFile('conf/settings', '-luaon -canSkip') or {},SETTING) and + pcall(TABLE.coverR,loadFile('conf/data', '-json -canSkip') or loadFile('conf/data', '-luaon -canSkip') or {},STAT) and + pcall(TABLE.cover, loadFile('conf/key', '-json -canSkip') or loadFile('conf/key', '-luaon -canSkip') or {},KEY_MAP) and + pcall(TABLE.cover, loadFile('conf/virtualkey','-json -canSkip') or loadFile('conf/virtualkey','-luaon -canSkip') or {},VK_ORG) + ) +then + MES.new('error',"An error occured during loading, and some data was lost.") end -table.insert(_LOADTIMELIST_,("Load Files: %.3fs"):format(TIME()-_LOADTIME_)) - ---Update data +-- Update data do - local needSave - - if not fs.getInfo('conf/data')then - needSave=true - end - if type(STAT.version)~='number'then + if type(STAT.version)~='number' then STAT.version=0 - needSave=true - end - if STAT.version<1500 then - FILE.clear_s('') - end - if STAT.version<1505 then - fs.remove('record/bigbang.rec') - fs.remove('conf/replay') - end - if STAT.version==1506 then - local temp1,temp2 - if fs.getInfo('record/master_l.rec')then - temp1=fs.read('record/master_l.rec') - end - if fs.getInfo('record/master_u.rec')then - temp2=fs.read('record/master_u.rec') - end - if temp1 then - fs.write('record/master_u.rec',temp1) - end - if temp2 then - fs.write('record/master_l.rec',temp2) - end - RANKS.master_l,RANKS.master_u=RANKS.master_u,RANKS.master_l - if RANKS.tsd_u then - RANKS.tsd_u=0 - end - end - if STAT.version==1601 then - RANKS.round_e=nil - RANKS.round_n=nil - RANKS.round_h=nil - RANKS.round_l=nil - RANKS.round_u=nil - fs.remove('record/round_e.rec') - fs.remove('record/round_n.rec') - fs.remove('record/round_h.rec') - fs.remove('record/round_l.rec') - fs.remove('record/round_u.rec') end if STAT.version<1700 and SETTING.dascut<5 then SETTING.dascut=SETTING.dascut+1 - needSave=true end + if RANKS.stack_e then - RANKS.stack_e=nil - RANKS.stack_h=nil - RANKS.stack_u=nil - fs.remove('record/stack_e.rec') - fs.remove('record/stack_h.rec') - fs.remove('record/stack_u.rec') + RANKS.stack_e=nil; fs.remove('record/stack_e.rec') + RANKS.stack_h=nil; fs.remove('record/stack_h.rec') + RANKS.stack_u=nil; fs.remove('record/stack_u.rec') end if RANKS.stack_20l then - RANKS.stack_20l=nil - RANKS.stack_40l=nil - RANKS.stack_100l=nil - fs.remove('record/stack_20l.rec') - fs.remove('record/stack_40l.rec') - fs.remove('record/stack_100l.rec') + RANKS.stack_20l=nil; fs.remove('record/stack_20l.rec') + RANKS.stack_40l=nil; fs.remove('record/stack_40l.rec') + RANKS.stack_100l=nil; fs.remove('record/stack_100l.rec') end if RANKS.rhythm_e then - RANKS.rhythm_e=nil - RANKS.rhythm_h=nil - RANKS.rhythm_u=nil - fs.remove('record/rhythm_e.rec') - fs.remove('record/rhythm_h.rec') - fs.remove('record/rhythm_u.rec') - end - if RANKS.bigbang or RANKS.clearRush then - fs.remove('record/clearRush.rec') - fs.remove('record/bigbang.rec') + RANKS.rhythm_e=nil; fs.remove('record/rhythm_e.rec') + RANKS.rhythm_h=nil; fs.remove('record/rhythm_h.rec') + RANKS.rhythm_u=nil; fs.remove('record/rhythm_u.rec') end + if RANKS.bigbang then RANKS.bigbang=nil; fs.remove('record/bigbang.rec') end + if RANKS.clearRush then RANKS.clearRush=nil; fs.remove('record/clearRush.rec') end + if RANKS.strategy_e then RANKS.strategy_e=nil; fs.remove('record/strategy_e.rec') end + if RANKS.strategy_h_plus then RANKS.strategy_h_plus=nil; fs.remove('record/strategy_h_plus.rec') end + if RANKS.strategy_u_plus then RANKS.strategy_u_plus=nil; fs.remove('record/strategy_u_plus.rec') end + + if STAT.version<1715 then fs.remove('record/dig_quad_10l.rec') end + if STAT.version~=VERSION.code then for k,v in next,MODE_UPDATE_MAP do - if RANKS[k]then + if RANKS[k] then RANKS[v]=RANKS[k] RANKS[k]=nil end k='record/'..k - if fs.getInfo(k..'.dat')then + if fs.getInfo(k..'.dat') then fs.write('record/'..v..'.rec',fs.read(k..'.dat')) fs.remove(k..'.dat') end - if fs.getInfo(k..'.rec')then + if fs.getInfo(k..'.rec') then fs.write('record/'..v..'.rec',fs.read(k..'.rec')) fs.remove(k..'.rec') end end STAT.version=VERSION.code - needSave=true end - SETTING.appLock,SETTING.dataSaving,SETTING.swap=nil + SETTING.appLock,SETTING.dataSaving,SETTING.swap,SETTING.autoLogin=nil if not SETTING.VKSkin then SETTING.VKSkin=1 end for _,v in next,SETTING.skin do if v<1 or v>17 then v=17 end end - if not RSlist[SETTING.RS]then SETTING.RS='TRS'end - if SETTING.ghostType=='greyCell'then SETTING.ghostType='grayCell'end - if type(SETTING.skinSet)=='number'then SETTING.skinSet='crystal_scf'end - if not TABLE.find({8,10,13,17,22,29,37,47,62,80,100},SETTING.frameMul)then SETTING.frameMul=100 end + if not RSlist[SETTING.RS] then SETTING.RS='TRS' end + if SETTING.ghostType=='greyCell' then SETTING.ghostType='grayCell' end + if type(SETTING.skinSet)=='number' then SETTING.skinSet='Crystal (Scf)' end + if string.find(SETTING.skinSet,"_") then SETTING.skinSet='Crystal (Scf)' end + if not TABLE.find({8,10,13,17,22,29,37,47,62,80,100},SETTING.frameMul) then SETTING.frameMul=100 end if SETTING.cv then SETTING.vocPack,SETTING.cv=SETTING.cv end - if type(SETTING.bg)~='string'then SETTING.bg='on'end + if type(SETTING.bg)~='string' then SETTING.bg='on' end if SETTING.skin[18]==10 then SETTING.skin[18]=4 end if SETTING.reTime>3 or SETTING.reTime<.5 then SETTING.reTime=2 end if SETTING.locale=='zh_full' then SETTING.locale='zh' end + if SETTING.vocPack=='rin' then SETTING.vocPack='miku' end + if SETTING.msaa>4 then SETTING.msaa=4 end if RANKS.infinite then RANKS.infinite=0 end if RANKS.infinite_dig then RANKS.infinite_dig=0 end if not RANKS.sprint_10l then RANKS.sprint_10l=0 end - if RANKS.master_l then RANKS.master_n,RANKS.master_l=RANKS.master_l needSave=true end - if RANKS.master_u then RANKS.master_h,RANKS.master_u=RANKS.master_u needSave=true end + if RANKS.master_l then RANKS.master_n,RANKS.master_l=RANKS.master_l end + if RANKS.master_u then RANKS.master_h,RANKS.master_u=RANKS.master_u end + if RANKS.secret_grade then RANKS.construct_sg,RANKS.secret_grade=RANKS.secret_grade end for _,v in next,VK_ORG do v.color=nil end for name,rank in next,RANKS do - if type(name)=='number'or type(rank)~='number'then + if type(name)=='number' or type(rank)~='number' then RANKS[name]=nil - needSave=true else local M=MODES[name] if M and M.unlock and rank>0 then for _,unlockName in next,M.unlock do - if not RANKS[unlockName]then + if not RANKS[unlockName] then RANKS[unlockName]=0 - needSave=true end end end - if not(M and M.x)then + if not (M and M.x) then RANKS[name]=nil - needSave=true end end end - if not MODES[STAT.lastPlay]then + if not MODES[STAT.lastPlay] then STAT.lastPlay='sprint_10l' - needSave=true end + fs.remove('conf/account') - if needSave then - saveStats() - saveProgress() - saveSettings() - love.event.quit('restart') + saveStats() + saveProgress() + saveSettings() +end + +-- Initialize language lib +LANG.init('zh', + { + zh=require'parts.language.lang_zh', + zh_trad=require'parts.language.lang_zh_trad', + en=require'parts.language.lang_en', + fr=require'parts.language.lang_fr', + es=require'parts.language.lang_es', + pt=require'parts.language.lang_pt', + id=require'parts.language.lang_id', + ja=require'parts.language.lang_ja', + ru=require'parts.language.lang_ru', + zh_code=require'parts.language.lang_zh_code', + vi=require'parts.language.lang_vi', + -- 1. Add language file to LANG folder; + -- 2. Require it; + -- 3. Add a button in parts/scenes/lang.lua; + }, + { + block=BLOCK_NAMES, + }, + (function() + local tipMeta={__call=function(L) return L[math.random(#L)] end} + return function(L) + if type(rawget(L,'getTip'))=='table' then setmetatable(L.getTip,tipMeta) end + setmetatable(L,{ + __index=function(self,k) + local mes="No Text ("..SETTING.locale.."): "..k + LOG(mes) + MES.new('warn',mes) + self[k]="["..k.."]" + return self[k] + end, + }) + end + end)() +) +if LANG.get(SETTING.locale)==LANG.get('zh') and SETTING.locale~='zh' then SETTING.locale='zh' end + +-- Load background files from SOURCE ONLY +for _,v in next,fs.getDirectoryItems('parts/backgrounds') do + if FILE.isSafe('parts/backgrounds/'..v) and v:sub(-3)=='lua' then + local name=v:sub(1,-5) + BG.add(name,require('parts.backgrounds.'..name)) + end +end +BG.remList('none') +BG.remList('gray') +BG.remList('custom') + +-- Load scene files from SOURCE ONLY +for _,v in next,fs.getDirectoryItems('parts/scenes') do + if FILE.isSafe('parts/scenes/'..v) then + local sceneName=v:sub(1,-5) + SCN.add(sceneName,require('parts.scenes.'..sceneName)) + LANG.addScene(sceneName) end end ---First start +table.insert(_LOADTIMELIST_,("Load Files: %.3fs"):format(TIME()-_LOADTIME_)) + +-- First start FIRSTLAUNCH=STAT.run==0 if FIRSTLAUNCH and MOBILE then SETTING.VKSwitch=true @@ -610,71 +548,89 @@ if FIRSTLAUNCH and MOBILE then SETTING.cleanCanvas=true end ---Apply system setting +-- Apply system setting applySettings() ---Load replays -for _,fileName in next,fs.getDirectoryItems('replay')do - if fileName:sub(12,12):match("[a-zA-Z]")then - local date,mode,version,player,seed,setting,mod - local fileData=fs.read('replay/'..fileName) - date, fileData=STRING.readLine(fileData)date=date:gsub("[a-zA-Z]","") - mode, fileData=STRING.readLine(fileData)mode=MODE_UPDATE_MAP[mode]or mode - version,fileData=STRING.readLine(fileData) - player, fileData=STRING.readLine(fileData)if player=="Local Player"then player="Stacker"end - local success - success,fileData=pcall(love.data.decompress,'string','zlib',fileData) - if not success then goto BREAK_cannotParse end - seed, fileData=STRING.readLine(fileData) - setting,fileData=STRING.readLine(fileData)setting=JSON.decode(setting) - mod, fileData=STRING.readLine(fileData)mod=JSON.decode(mod) - if - not setting or - not mod or - not mode or - #mode==0 - then goto BREAK_cannotParse end - - fs.remove('replay/'..fileName) - local newName=fileName:sub(1,10)..fileName:sub(15) - fs.write('replay/'..newName, - love.data.compress('string','zlib', - JSON.encode{ - date=date, - mode=mode, - version=version, - player=player, - seed=seed, - setting=setting, - mod=mod, - }.."\n".. - fileData +-- Load replays +for _,fileName in next,fs.getDirectoryItems('replay') do + if fileName:sub(12,12):match("[a-zA-Z]") then + repeat + local date,mode,version,player,seed,setting,mod + local success,fileData=true,fs.read('replay/'..fileName) + date,fileData=STRING.readLine(fileData) + date=date:gsub("[a-zA-Z]","") + mode,fileData=STRING.readLine(fileData) + mode=MODE_UPDATE_MAP[mode] or mode + version,fileData=STRING.readLine(fileData) + player,fileData=STRING.readLine(fileData) + if player=="Local Player" then player="Stacker" end + success,fileData=pcall(love.data.decompress,'string','zlib',fileData) + if not success then break end + seed,fileData=STRING.readLine(fileData) + setting,fileData=STRING.readLine(fileData) + setting=JSON.decode(setting) + mod,fileData=STRING.readLine(fileData) + mod=JSON.decode(mod) + if + not setting or + not mod or + not mode or + #mode==0 + then + break + end + + fs.remove('replay/'..fileName) + local newName=fileName:sub(1,10)..fileName:sub(15) + fs.write('replay/'..newName, + love.data.compress('string','zlib', + JSON.encode{ + date=date, + mode=mode, + version=version, + player=player, + seed=seed, + setting=setting, + mod=mod, + }.."\n".. + fileData + ) ) - ) - fileName=newName + fileName=newName + until true end - ::BREAK_cannotParse:: local rep=DATA.parseReplay('replay/'..fileName) table.insert(REPLAY,rep) end -table.sort(REPLAY,function(a,b)return a.fileName>b.fileName end) +table.sort(REPLAY,function(a,b) return a.fileName>b.fileName end) + +AUTHURL="https://www.studio26f.org/oauth?product=techmino" +AUTHHOST="www.studio26f.org:8080" +WS.switchHost('www.studio26f.org','8081','/techmino/ws/v1') +HTTP.setHost("www.studio26f.org:8081") +HTTP.setThreadCount(1) -table.insert(_LOADTIMELIST_,("Initialize Data: %.3fs"):format(TIME()-_LOADTIME_)) +-- Discord RPC +DiscordRPC=require'parts.discordRPC' +DiscordRPC.update() -for i=1,#_LOADTIMELIST_ do LOG(_LOADTIMELIST_[i])end +table.insert(_LOADTIMELIST_,("Load Resources: %.3fs"):format(TIME()-_LOADTIME_)) ---Launch testing task if launch param received -if TABLE.find(arg,'--test')then +for i=1,#_LOADTIMELIST_ do LOG(_LOADTIMELIST_[i]) end + +-- Launch testing task if launch param received +if TABLE.find(arg,'--test') then TASK.new(function() - while not LOADED do YIELD()end + while not LOADED do coroutine.yield() end LOG("\27[92m\27[1mAutomatic Test Started\27[0m") - BGM.setVol(0)SFX.setVol(0) + BGM.setVol(0) + SFX.setVol(0) love.keypressed('space') TEST.yieldUntilNextScene() for k,mode in next,MODES do - if k~='netBattle'then + if k~='netBattle' then LOG("Scanning mode: "..mode.name) loadGame(mode.name,true) TEST.yieldUntilNextScene() @@ -688,12 +644,11 @@ if TABLE.find(arg,'--test')then end) TASK.new(function() while true do - YIELD() - if Z.getErr(1)then break end + coroutine.yield() + if Z.getErr(1) then break end end LOG("\27[91m\27[1mAutomatic Test Failed :(\27[0m\nThe error message is:\n"..table.concat(Z.getErr(1).mes,"\n").."\27[91m\nAborting\27[0m") TEST.yieldN(60) love.event.quit(1) end) end -WS.switchHost('101.43.110.22','10026','/tech/socket/v1') diff --git a/media/effect/chiptune/error.ogg b/media/effect/chiptune/error.ogg index e2a135ad1..f04ff295b 100644 Binary files a/media/effect/chiptune/error.ogg and b/media/effect/chiptune/error.ogg differ diff --git a/media/effect/chiptune/mod_off.ogg b/media/effect/chiptune/mod_off.ogg new file mode 100644 index 000000000..22183d9d9 Binary files /dev/null and b/media/effect/chiptune/mod_off.ogg differ diff --git a/media/effect/chiptune/mod_on.ogg b/media/effect/chiptune/mod_on.ogg new file mode 100644 index 000000000..9f88ef11b Binary files /dev/null and b/media/effect/chiptune/mod_on.ogg differ diff --git a/media/image/characters/flore.png b/media/image/characters/flore.png new file mode 100644 index 000000000..923c330f8 Binary files /dev/null and b/media/image/characters/flore.png differ diff --git a/media/image/characters/miku.png b/media/image/characters/miku.png index e1fa2c1f9..f1238afb9 100644 Binary files a/media/image/characters/miku.png and b/media/image/characters/miku.png differ diff --git a/media/image/characters/neuro.png b/media/image/characters/neuro.png new file mode 100644 index 000000000..79ed5d9be Binary files /dev/null and b/media/image/characters/neuro.png differ diff --git a/media/image/characters/zundamon.png b/media/image/characters/zundamon.png new file mode 100644 index 000000000..1f4a06965 Binary files /dev/null and b/media/image/characters/zundamon.png differ diff --git a/media/image/mess/speedLimit.png b/media/image/mess/speedLimit.png deleted file mode 100644 index ceb602b54..000000000 Binary files a/media/image/mess/speedLimit.png and /dev/null differ diff --git a/media/image/modeicon/big.png b/media/image/modeicon/big.png deleted file mode 100644 index 3a6a234d0..000000000 Binary files a/media/image/modeicon/big.png and /dev/null differ diff --git a/media/image/modeicon/bigbang.png b/media/image/modeicon/bigbang.png deleted file mode 100644 index 8eb09cc89..000000000 Binary files a/media/image/modeicon/bigbang.png and /dev/null differ diff --git a/media/image/modeicon/hidden.png b/media/image/modeicon/hidden.png index 72c5d02d7..10b765e51 100644 Binary files a/media/image/modeicon/hidden.png and b/media/image/modeicon/hidden.png differ diff --git a/media/image/modeicon/hidden2.png b/media/image/modeicon/hidden2.png index 8b908f95e..ae6504d49 100644 Binary files a/media/image/modeicon/hidden2.png and b/media/image/modeicon/hidden2.png differ diff --git a/media/image/modeicon/hidden3.png b/media/image/modeicon/hidden3.png new file mode 100644 index 000000000..33c3939fc Binary files /dev/null and b/media/image/modeicon/hidden3.png differ diff --git a/media/image/modeicon/hidden4.png b/media/image/modeicon/hidden4.png new file mode 100644 index 000000000..7d94b2f46 Binary files /dev/null and b/media/image/modeicon/hidden4.png differ diff --git a/media/image/modeicon/hidden5.png b/media/image/modeicon/hidden5.png new file mode 100644 index 000000000..edeea358a Binary files /dev/null and b/media/image/modeicon/hidden5.png differ diff --git a/media/image/modeicon/note.txt b/media/image/modeicon/note.txt new file mode 100644 index 000000000..2b0e77e75 --- /dev/null +++ b/media/image/modeicon/note.txt @@ -0,0 +1,2 @@ +If the modeicon you're looking for isn't here, try looking in /parts/scenes/load.lua. +We vectorized some of them for better scalability. \ No newline at end of file diff --git a/media/image/modeicon/rhythm.png b/media/image/modeicon/rhythm.png deleted file mode 100644 index 32ac4084b..000000000 Binary files a/media/image/modeicon/rhythm.png and /dev/null differ diff --git a/media/image/modeicon/secret_grade.png b/media/image/modeicon/secret_grade.png deleted file mode 100644 index bb3d7ec21..000000000 Binary files a/media/image/modeicon/secret_grade.png and /dev/null differ diff --git a/media/image/modeicon/sprint_pento.png b/media/image/modeicon/sprint_pento.png deleted file mode 100644 index aea62da5a..000000000 Binary files a/media/image/modeicon/sprint_pento.png and /dev/null differ diff --git a/media/image/modeicon/sprint_tri.png b/media/image/modeicon/sprint_tri.png deleted file mode 100644 index 6203e946f..000000000 Binary files a/media/image/modeicon/sprint_tri.png and /dev/null differ diff --git a/media/image/modeicon/tech.png b/media/image/modeicon/tech.png deleted file mode 100644 index 44494d72e..000000000 Binary files a/media/image/modeicon/tech.png and /dev/null differ diff --git a/media/image/modeicon/ultra.png b/media/image/modeicon/ultra.png deleted file mode 100644 index 262c94510..000000000 Binary files a/media/image/modeicon/ultra.png and /dev/null differ diff --git a/media/image/modeicon/zen.png b/media/image/modeicon/zen.png deleted file mode 100644 index fdf9b5a86..000000000 Binary files a/media/image/modeicon/zen.png and /dev/null differ diff --git a/media/image/skin/asriel/arcade.png b/media/image/skin/asriel/arcade.png new file mode 100755 index 000000000..1af2600c1 Binary files /dev/null and b/media/image/skin/asriel/arcade.png differ diff --git a/media/image/skin/asriel/cardboard.png b/media/image/skin/asriel/cardboard.png new file mode 100644 index 000000000..f0385efd8 Binary files /dev/null and b/media/image/skin/asriel/cardboard.png differ diff --git a/media/image/skin/chno/kanji.png b/media/image/skin/chno/kanji.png new file mode 100644 index 000000000..2f632dfd7 Binary files /dev/null and b/media/image/skin/chno/kanji.png differ diff --git a/media/image/skin/chno/letters.png b/media/image/skin/chno/letters.png new file mode 100644 index 000000000..3cb9b1d37 Binary files /dev/null and b/media/image/skin/chno/letters.png differ diff --git a/media/image/skin/pastel_chno.png b/media/image/skin/chno/pastel.png similarity index 100% rename from media/image/skin/pastel_chno.png rename to media/image/skin/chno/pastel.png diff --git a/media/image/skin/pixel_chno.png b/media/image/skin/chno/pixel.png similarity index 100% rename from media/image/skin/pixel_chno.png rename to media/image/skin/chno/pixel.png diff --git a/media/image/skin/shiny_chno.png b/media/image/skin/chno/shiny.png similarity index 100% rename from media/image/skin/shiny_chno.png rename to media/image/skin/chno/shiny.png diff --git a/media/image/skin/cartooncup_earety.png b/media/image/skin/earety/cartooncup.png similarity index 100% rename from media/image/skin/cartooncup_earety.png rename to media/image/skin/earety/cartooncup.png diff --git a/media/image/skin/kanji_chno.png b/media/image/skin/kanji_chno.png deleted file mode 100644 index 63d5c88db..000000000 Binary files a/media/image/skin/kanji_chno.png and /dev/null differ diff --git a/media/image/skin/letters_chno.png b/media/image/skin/letters_chno.png deleted file mode 100644 index 61847b649..000000000 Binary files a/media/image/skin/letters_chno.png and /dev/null differ diff --git a/media/image/skin/jelly_miya.png b/media/image/skin/miya/jelly.png similarity index 100% rename from media/image/skin/jelly_miya.png rename to media/image/skin/miya/jelly.png diff --git a/media/image/skin/coloredbone_mrz.png b/media/image/skin/mrz/coloredbone.png similarity index 100% rename from media/image/skin/coloredbone_mrz.png rename to media/image/skin/mrz/coloredbone.png diff --git a/media/image/skin/contrast_mrz.png b/media/image/skin/mrz/contrast.png similarity index 100% rename from media/image/skin/contrast_mrz.png rename to media/image/skin/mrz/contrast.png diff --git a/media/image/skin/glow_mrz.png b/media/image/skin/mrz/glow.png similarity index 100% rename from media/image/skin/glow_mrz.png rename to media/image/skin/mrz/glow.png diff --git a/media/image/skin/matte_mrz.png b/media/image/skin/mrz/matte.png similarity index 100% rename from media/image/skin/matte_mrz.png rename to media/image/skin/mrz/matte.png diff --git a/media/image/skin/paper_mrz.png b/media/image/skin/mrz/paper.png similarity index 100% rename from media/image/skin/paper_mrz.png rename to media/image/skin/mrz/paper.png diff --git a/media/image/skin/plastic_mrz.png b/media/image/skin/mrz/plastic.png similarity index 100% rename from media/image/skin/plastic_mrz.png rename to media/image/skin/mrz/plastic.png diff --git a/media/image/skin/pure_mrz.png b/media/image/skin/mrz/pure.png similarity index 100% rename from media/image/skin/pure_mrz.png rename to media/image/skin/mrz/pure.png diff --git a/media/image/skin/smooth_mrz.png b/media/image/skin/mrz/smooth.png similarity index 100% rename from media/image/skin/smooth_mrz.png rename to media/image/skin/mrz/smooth.png diff --git a/media/image/skin/textbone_mrz.png b/media/image/skin/mrz/textbone.png similarity index 100% rename from media/image/skin/textbone_mrz.png rename to media/image/skin/mrz/textbone.png diff --git a/media/image/skin/wtf_mrz.png b/media/image/skin/mrz/wtf.png similarity index 100% rename from media/image/skin/wtf_mrz.png rename to media/image/skin/mrz/wtf.png diff --git a/media/image/skin/brick_notypey.png b/media/image/skin/notypey/brick.png similarity index 100% rename from media/image/skin/brick_notypey.png rename to media/image/skin/notypey/brick.png diff --git a/media/image/skin/gem_notypey.png b/media/image/skin/notypey/gem.png similarity index 100% rename from media/image/skin/gem_notypey.png rename to media/image/skin/notypey/gem.png diff --git a/media/image/skin/retro_notypey.png b/media/image/skin/notypey/retro.png similarity index 100% rename from media/image/skin/retro_notypey.png rename to media/image/skin/notypey/retro.png diff --git a/media/image/skin/bright_scf.png b/media/image/skin/scf/bright.png similarity index 100% rename from media/image/skin/bright_scf.png rename to media/image/skin/scf/bright.png diff --git a/media/image/skin/bubble_scf.png b/media/image/skin/scf/bubble.png similarity index 100% rename from media/image/skin/bubble_scf.png rename to media/image/skin/scf/bubble.png diff --git a/media/image/skin/crystal_scf.png b/media/image/skin/scf/crystal.png similarity index 100% rename from media/image/skin/crystal_scf.png rename to media/image/skin/scf/crystal.png diff --git a/media/image/skin/glass_scf.png b/media/image/skin/scf/glass.png similarity index 100% rename from media/image/skin/glass_scf.png rename to media/image/skin/scf/glass.png diff --git a/media/image/skin/minoes_scf.png b/media/image/skin/scf/minoes.png similarity index 100% rename from media/image/skin/minoes_scf.png rename to media/image/skin/scf/minoes.png diff --git a/media/image/skin/penta_scf.png b/media/image/skin/scf/penta.png similarity index 100% rename from media/image/skin/penta_scf.png rename to media/image/skin/scf/penta.png diff --git a/media/image/skin/polkadots_scf.png b/media/image/skin/scf/polkadots.png similarity index 100% rename from media/image/skin/polkadots_scf.png rename to media/image/skin/scf/polkadots.png diff --git a/media/image/skin/simple_scf.png b/media/image/skin/scf/simple.png similarity index 100% rename from media/image/skin/simple_scf.png rename to media/image/skin/scf/simple.png diff --git a/media/image/skin/toy_scf.png b/media/image/skin/scf/toy.png similarity index 100% rename from media/image/skin/toy_scf.png rename to media/image/skin/scf/toy.png diff --git a/media/image/skin/yinyang_scf.png b/media/image/skin/scf/yinyang.png similarity index 100% rename from media/image/skin/yinyang_scf.png rename to media/image/skin/scf/yinyang.png diff --git a/media/image/skin/ball_shaw.png b/media/image/skin/shaw/ball.png similarity index 100% rename from media/image/skin/ball_shaw.png rename to media/image/skin/shaw/ball.png diff --git a/media/image/skin/classic_unknown.png b/media/image/skin/unknown/classic.png similarity index 100% rename from media/image/skin/classic_unknown.png rename to media/image/skin/unknown/classic.png diff --git a/media/image/virtualkey.png b/media/image/virtualkey.png index eb6a6f69c..71e1cc6b6 100644 Binary files a/media/image/virtualkey.png and b/media/image/virtualkey.png differ diff --git a/media/music/antispace.ogg b/media/music/antispace.ogg new file mode 100644 index 000000000..f2b8d2384 Binary files /dev/null and b/media/music/antispace.ogg differ diff --git a/media/music/way.ogg b/media/music/way.ogg index d501b7cbc..b70240264 100644 Binary files a/media/music/way.ogg and b/media/music/way.ogg differ diff --git a/media/vocal/flore/b2b_1.ogg b/media/vocal/flore/b2b_1.ogg new file mode 100644 index 000000000..397710011 Binary files /dev/null and b/media/vocal/flore/b2b_1.ogg differ diff --git a/media/vocal/flore/b2b_2.ogg b/media/vocal/flore/b2b_2.ogg new file mode 100644 index 000000000..37e79559e Binary files /dev/null and b/media/vocal/flore/b2b_2.ogg differ diff --git a/media/vocal/flore/b2b_3.ogg b/media/vocal/flore/b2b_3.ogg new file mode 100644 index 000000000..b8682b78f Binary files /dev/null and b/media/vocal/flore/b2b_3.ogg differ diff --git a/media/vocal/flore/b3b_1.ogg b/media/vocal/flore/b3b_1.ogg new file mode 100644 index 000000000..cb2d7758c Binary files /dev/null and b/media/vocal/flore/b3b_1.ogg differ diff --git a/media/vocal/flore/b3b_2.ogg b/media/vocal/flore/b3b_2.ogg new file mode 100644 index 000000000..24ef6a62b Binary files /dev/null and b/media/vocal/flore/b3b_2.ogg differ diff --git a/media/vocal/flore/b3b_3.ogg b/media/vocal/flore/b3b_3.ogg new file mode 100644 index 000000000..24d80be76 Binary files /dev/null and b/media/vocal/flore/b3b_3.ogg differ diff --git a/media/vocal/flore/bye_1.ogg b/media/vocal/flore/bye_1.ogg new file mode 100644 index 000000000..17234bffd Binary files /dev/null and b/media/vocal/flore/bye_1.ogg differ diff --git a/media/vocal/flore/bye_2.ogg b/media/vocal/flore/bye_2.ogg new file mode 100644 index 000000000..44608266a Binary files /dev/null and b/media/vocal/flore/bye_2.ogg differ diff --git a/media/vocal/flore/bye_3.ogg b/media/vocal/flore/bye_3.ogg new file mode 100644 index 000000000..c55ad2ff2 Binary files /dev/null and b/media/vocal/flore/bye_3.ogg differ diff --git a/media/vocal/flore/bye_4.ogg b/media/vocal/flore/bye_4.ogg new file mode 100644 index 000000000..8bd4d2e3f Binary files /dev/null and b/media/vocal/flore/bye_4.ogg differ diff --git a/media/vocal/flore/bye_5.ogg b/media/vocal/flore/bye_5.ogg new file mode 100644 index 000000000..f7dde5656 Binary files /dev/null and b/media/vocal/flore/bye_5.ogg differ diff --git a/media/vocal/flore/bye_6.ogg b/media/vocal/flore/bye_6.ogg new file mode 100644 index 000000000..96ce043fc Binary files /dev/null and b/media/vocal/flore/bye_6.ogg differ diff --git a/media/vocal/flore/cspin.ogg b/media/vocal/flore/cspin.ogg new file mode 100644 index 000000000..a0618211f Binary files /dev/null and b/media/vocal/flore/cspin.ogg differ diff --git a/media/vocal/flore/decacrash_1.ogg b/media/vocal/flore/decacrash_1.ogg new file mode 100644 index 000000000..a57b83324 Binary files /dev/null and b/media/vocal/flore/decacrash_1.ogg differ diff --git a/media/vocal/flore/decacrash_2.ogg b/media/vocal/flore/decacrash_2.ogg new file mode 100644 index 000000000..abb739252 Binary files /dev/null and b/media/vocal/flore/decacrash_2.ogg differ diff --git a/media/vocal/flore/dodecacrash_1.ogg b/media/vocal/flore/dodecacrash_1.ogg new file mode 100644 index 000000000..ba192c29e Binary files /dev/null and b/media/vocal/flore/dodecacrash_1.ogg differ diff --git a/media/vocal/flore/double_1.ogg b/media/vocal/flore/double_1.ogg new file mode 100644 index 000000000..f7c33ffc8 Binary files /dev/null and b/media/vocal/flore/double_1.ogg differ diff --git a/media/vocal/flore/double_2.ogg b/media/vocal/flore/double_2.ogg new file mode 100644 index 000000000..9817a2beb Binary files /dev/null and b/media/vocal/flore/double_2.ogg differ diff --git a/media/vocal/flore/double_3.ogg b/media/vocal/flore/double_3.ogg new file mode 100644 index 000000000..dbe251eb9 Binary files /dev/null and b/media/vocal/flore/double_3.ogg differ diff --git a/media/vocal/flore/doubt_1.ogg b/media/vocal/flore/doubt_1.ogg new file mode 100644 index 000000000..9453f8601 Binary files /dev/null and b/media/vocal/flore/doubt_1.ogg differ diff --git a/media/effect/chiptune/uncheck .ogg b/media/vocal/flore/doubt_2.ogg similarity index 57% rename from media/effect/chiptune/uncheck .ogg rename to media/vocal/flore/doubt_2.ogg index 6665d8bb1..c615a5212 100644 Binary files a/media/effect/chiptune/uncheck .ogg and b/media/vocal/flore/doubt_2.ogg differ diff --git a/media/vocal/flore/doubt_3.ogg b/media/vocal/flore/doubt_3.ogg new file mode 100644 index 000000000..bb67115b3 Binary files /dev/null and b/media/vocal/flore/doubt_3.ogg differ diff --git a/media/vocal/flore/espin.ogg b/media/vocal/flore/espin.ogg new file mode 100644 index 000000000..34ea1fe6b Binary files /dev/null and b/media/vocal/flore/espin.ogg differ diff --git a/media/vocal/flore/fspin.ogg b/media/vocal/flore/fspin.ogg new file mode 100644 index 000000000..e4647701e Binary files /dev/null and b/media/vocal/flore/fspin.ogg differ diff --git a/media/vocal/flore/half_clear_1.ogg b/media/vocal/flore/half_clear_1.ogg new file mode 100644 index 000000000..7ba62f7bb Binary files /dev/null and b/media/vocal/flore/half_clear_1.ogg differ diff --git a/media/vocal/flore/half_clear_2.ogg b/media/vocal/flore/half_clear_2.ogg new file mode 100644 index 000000000..00dbd0c5a Binary files /dev/null and b/media/vocal/flore/half_clear_2.ogg differ diff --git a/media/vocal/flore/happy_1.ogg b/media/vocal/flore/happy_1.ogg new file mode 100644 index 000000000..925aa3f24 Binary files /dev/null and b/media/vocal/flore/happy_1.ogg differ diff --git a/media/vocal/flore/happy_2.ogg b/media/vocal/flore/happy_2.ogg new file mode 100644 index 000000000..aa283dacb Binary files /dev/null and b/media/vocal/flore/happy_2.ogg differ diff --git a/media/vocal/flore/happy_3.ogg b/media/vocal/flore/happy_3.ogg new file mode 100644 index 000000000..bf653259f Binary files /dev/null and b/media/vocal/flore/happy_3.ogg differ diff --git a/media/vocal/flore/heptacrash_1.ogg b/media/vocal/flore/heptacrash_1.ogg new file mode 100644 index 000000000..8895a1da2 Binary files /dev/null and b/media/vocal/flore/heptacrash_1.ogg differ diff --git a/media/vocal/flore/heptacrash_2.ogg b/media/vocal/flore/heptacrash_2.ogg new file mode 100644 index 000000000..e8dea3b0e Binary files /dev/null and b/media/vocal/flore/heptacrash_2.ogg differ diff --git a/media/vocal/flore/heptadecacrash_1.ogg b/media/vocal/flore/heptadecacrash_1.ogg new file mode 100644 index 000000000..4e350a2ac Binary files /dev/null and b/media/vocal/flore/heptadecacrash_1.ogg differ diff --git a/media/vocal/flore/hexacrash_1.ogg b/media/vocal/flore/hexacrash_1.ogg new file mode 100644 index 000000000..184074fcc Binary files /dev/null and b/media/vocal/flore/hexacrash_1.ogg differ diff --git a/media/vocal/flore/hexacrash_2.ogg b/media/vocal/flore/hexacrash_2.ogg new file mode 100644 index 000000000..761057bfd Binary files /dev/null and b/media/vocal/flore/hexacrash_2.ogg differ diff --git a/media/vocal/flore/hexacrash_3.ogg b/media/vocal/flore/hexacrash_3.ogg new file mode 100644 index 000000000..a5dfbb61d Binary files /dev/null and b/media/vocal/flore/hexacrash_3.ogg differ diff --git a/media/vocal/flore/hexadecacrash_1.ogg b/media/vocal/flore/hexadecacrash_1.ogg new file mode 100644 index 000000000..34e1be53c Binary files /dev/null and b/media/vocal/flore/hexadecacrash_1.ogg differ diff --git a/media/vocal/flore/hspin.ogg b/media/vocal/flore/hspin.ogg new file mode 100644 index 000000000..49d212da7 Binary files /dev/null and b/media/vocal/flore/hspin.ogg differ diff --git a/media/vocal/flore/impossicrash_1.ogg b/media/vocal/flore/impossicrash_1.ogg new file mode 100644 index 000000000..ae159a37a Binary files /dev/null and b/media/vocal/flore/impossicrash_1.ogg differ diff --git a/media/vocal/flore/ispin_1.ogg b/media/vocal/flore/ispin_1.ogg new file mode 100644 index 000000000..003f7f532 Binary files /dev/null and b/media/vocal/flore/ispin_1.ogg differ diff --git a/media/vocal/flore/ispin_2.ogg b/media/vocal/flore/ispin_2.ogg new file mode 100644 index 000000000..fd0ca1b8c Binary files /dev/null and b/media/vocal/flore/ispin_2.ogg differ diff --git a/media/vocal/flore/jspin_1.ogg b/media/vocal/flore/jspin_1.ogg new file mode 100644 index 000000000..1b37611ec Binary files /dev/null and b/media/vocal/flore/jspin_1.ogg differ diff --git a/media/vocal/flore/jspin_2.ogg b/media/vocal/flore/jspin_2.ogg new file mode 100644 index 000000000..197c61c1e Binary files /dev/null and b/media/vocal/flore/jspin_2.ogg differ diff --git a/media/vocal/flore/lose_1.ogg b/media/vocal/flore/lose_1.ogg new file mode 100644 index 000000000..0032572d6 Binary files /dev/null and b/media/vocal/flore/lose_1.ogg differ diff --git a/media/vocal/flore/lose_2.ogg b/media/vocal/flore/lose_2.ogg new file mode 100644 index 000000000..80d86e2bf Binary files /dev/null and b/media/vocal/flore/lose_2.ogg differ diff --git a/media/vocal/flore/lose_3.ogg b/media/vocal/flore/lose_3.ogg new file mode 100644 index 000000000..1fa9164ef Binary files /dev/null and b/media/vocal/flore/lose_3.ogg differ diff --git a/media/vocal/flore/lose_4.ogg b/media/vocal/flore/lose_4.ogg new file mode 100644 index 000000000..06b8f33af Binary files /dev/null and b/media/vocal/flore/lose_4.ogg differ diff --git a/media/vocal/flore/lose_5.ogg b/media/vocal/flore/lose_5.ogg new file mode 100644 index 000000000..427ed6b65 Binary files /dev/null and b/media/vocal/flore/lose_5.ogg differ diff --git a/media/vocal/flore/lose_6.ogg b/media/vocal/flore/lose_6.ogg new file mode 100644 index 000000000..cb1340013 Binary files /dev/null and b/media/vocal/flore/lose_6.ogg differ diff --git a/media/vocal/flore/lspin_1.ogg b/media/vocal/flore/lspin_1.ogg new file mode 100644 index 000000000..fdb09d7d3 Binary files /dev/null and b/media/vocal/flore/lspin_1.ogg differ diff --git a/media/vocal/flore/lspin_2.ogg b/media/vocal/flore/lspin_2.ogg new file mode 100644 index 000000000..e949a8f63 Binary files /dev/null and b/media/vocal/flore/lspin_2.ogg differ diff --git a/media/vocal/flore/mini_1.ogg b/media/vocal/flore/mini_1.ogg new file mode 100644 index 000000000..00c0fae27 Binary files /dev/null and b/media/vocal/flore/mini_1.ogg differ diff --git a/media/vocal/flore/mini_2.ogg b/media/vocal/flore/mini_2.ogg new file mode 100644 index 000000000..e416a5661 Binary files /dev/null and b/media/vocal/flore/mini_2.ogg differ diff --git a/media/vocal/flore/mini_3.ogg b/media/vocal/flore/mini_3.ogg new file mode 100644 index 000000000..36d827d7f Binary files /dev/null and b/media/vocal/flore/mini_3.ogg differ diff --git a/media/vocal/flore/nonacrash_1.ogg b/media/vocal/flore/nonacrash_1.ogg new file mode 100644 index 000000000..2f9e8a52a Binary files /dev/null and b/media/vocal/flore/nonacrash_1.ogg differ diff --git a/media/vocal/flore/nonacrash_2.ogg b/media/vocal/flore/nonacrash_2.ogg new file mode 100644 index 000000000..6f2c159d3 Binary files /dev/null and b/media/vocal/flore/nonacrash_2.ogg differ diff --git a/media/vocal/flore/nonadecacrash_1.ogg b/media/vocal/flore/nonadecacrash_1.ogg new file mode 100644 index 000000000..5e2f5cc96 Binary files /dev/null and b/media/vocal/flore/nonadecacrash_1.ogg differ diff --git a/media/vocal/flore/nspin.ogg b/media/vocal/flore/nspin.ogg new file mode 100644 index 000000000..11b7f6fa5 Binary files /dev/null and b/media/vocal/flore/nspin.ogg differ diff --git a/media/vocal/flore/octacrash_1.ogg b/media/vocal/flore/octacrash_1.ogg new file mode 100644 index 000000000..a41ae17cc Binary files /dev/null and b/media/vocal/flore/octacrash_1.ogg differ diff --git a/media/vocal/flore/octacrash_2.ogg b/media/vocal/flore/octacrash_2.ogg new file mode 100644 index 000000000..97d357c06 Binary files /dev/null and b/media/vocal/flore/octacrash_2.ogg differ diff --git a/media/vocal/flore/octadecacrash_1.ogg b/media/vocal/flore/octadecacrash_1.ogg new file mode 100644 index 000000000..da9273b5b Binary files /dev/null and b/media/vocal/flore/octadecacrash_1.ogg differ diff --git a/media/vocal/flore/ospin.ogg b/media/vocal/flore/ospin.ogg new file mode 100644 index 000000000..437a1a892 Binary files /dev/null and b/media/vocal/flore/ospin.ogg differ diff --git a/media/vocal/flore/ospin_1.ogg b/media/vocal/flore/ospin_1.ogg new file mode 100644 index 000000000..042de6a22 Binary files /dev/null and b/media/vocal/flore/ospin_1.ogg differ diff --git a/media/vocal/flore/pentacrash_1.ogg b/media/vocal/flore/pentacrash_1.ogg new file mode 100644 index 000000000..45b96ce78 Binary files /dev/null and b/media/vocal/flore/pentacrash_1.ogg differ diff --git a/media/vocal/flore/pentacrash_2.ogg b/media/vocal/flore/pentacrash_2.ogg new file mode 100644 index 000000000..71c94804a Binary files /dev/null and b/media/vocal/flore/pentacrash_2.ogg differ diff --git a/media/vocal/flore/pentacrash_3.ogg b/media/vocal/flore/pentacrash_3.ogg new file mode 100644 index 000000000..c16f7918c Binary files /dev/null and b/media/vocal/flore/pentacrash_3.ogg differ diff --git a/media/vocal/flore/pentadecacrash_1.ogg b/media/vocal/flore/pentadecacrash_1.ogg new file mode 100644 index 000000000..187af33f4 Binary files /dev/null and b/media/vocal/flore/pentadecacrash_1.ogg differ diff --git a/media/vocal/flore/perfect_clear_1.ogg b/media/vocal/flore/perfect_clear_1.ogg new file mode 100644 index 000000000..769c66af1 Binary files /dev/null and b/media/vocal/flore/perfect_clear_1.ogg differ diff --git a/media/vocal/flore/perfect_clear_2.ogg b/media/vocal/flore/perfect_clear_2.ogg new file mode 100644 index 000000000..5aafb828b Binary files /dev/null and b/media/vocal/flore/perfect_clear_2.ogg differ diff --git a/media/vocal/flore/pspin.ogg b/media/vocal/flore/pspin.ogg new file mode 100644 index 000000000..0995e5075 Binary files /dev/null and b/media/vocal/flore/pspin.ogg differ diff --git a/media/vocal/flore/qspin.ogg b/media/vocal/flore/qspin.ogg new file mode 100644 index 000000000..1a2b466b1 Binary files /dev/null and b/media/vocal/flore/qspin.ogg differ diff --git a/media/vocal/flore/rspin.ogg b/media/vocal/flore/rspin.ogg new file mode 100644 index 000000000..0d4c65c2e Binary files /dev/null and b/media/vocal/flore/rspin.ogg differ diff --git a/media/vocal/flore/single_1.ogg b/media/vocal/flore/single_1.ogg new file mode 100644 index 000000000..392b5a89f Binary files /dev/null and b/media/vocal/flore/single_1.ogg differ diff --git a/media/vocal/flore/single_2.ogg b/media/vocal/flore/single_2.ogg new file mode 100644 index 000000000..eb8554f0a Binary files /dev/null and b/media/vocal/flore/single_2.ogg differ diff --git a/media/vocal/flore/single_3.ogg b/media/vocal/flore/single_3.ogg new file mode 100644 index 000000000..048f871fa Binary files /dev/null and b/media/vocal/flore/single_3.ogg differ diff --git a/media/vocal/flore/sspin_1.ogg b/media/vocal/flore/sspin_1.ogg new file mode 100644 index 000000000..670e30474 Binary files /dev/null and b/media/vocal/flore/sspin_1.ogg differ diff --git a/media/vocal/flore/sspin_2.ogg b/media/vocal/flore/sspin_2.ogg new file mode 100644 index 000000000..61125ae6a Binary files /dev/null and b/media/vocal/flore/sspin_2.ogg differ diff --git a/media/vocal/flore/techrash_1.ogg b/media/vocal/flore/techrash_1.ogg new file mode 100644 index 000000000..2cfe9e43f Binary files /dev/null and b/media/vocal/flore/techrash_1.ogg differ diff --git a/media/vocal/flore/techrash_2.ogg b/media/vocal/flore/techrash_2.ogg new file mode 100644 index 000000000..c760b6d19 Binary files /dev/null and b/media/vocal/flore/techrash_2.ogg differ diff --git a/media/vocal/flore/techrash_3.ogg b/media/vocal/flore/techrash_3.ogg new file mode 100644 index 000000000..6c74f9381 Binary files /dev/null and b/media/vocal/flore/techrash_3.ogg differ diff --git a/media/vocal/flore/test_1.ogg b/media/vocal/flore/test_1.ogg new file mode 100644 index 000000000..37fb7073a Binary files /dev/null and b/media/vocal/flore/test_1.ogg differ diff --git a/media/vocal/flore/test_2.ogg b/media/vocal/flore/test_2.ogg new file mode 100644 index 000000000..9d608c756 Binary files /dev/null and b/media/vocal/flore/test_2.ogg differ diff --git a/media/vocal/flore/test_3.ogg b/media/vocal/flore/test_3.ogg new file mode 100644 index 000000000..966a4b8bb Binary files /dev/null and b/media/vocal/flore/test_3.ogg differ diff --git a/media/vocal/flore/test_4.ogg b/media/vocal/flore/test_4.ogg new file mode 100644 index 000000000..31d4f4206 Binary files /dev/null and b/media/vocal/flore/test_4.ogg differ diff --git a/media/vocal/flore/tetradecacrash_1.ogg b/media/vocal/flore/tetradecacrash_1.ogg new file mode 100644 index 000000000..23a0cf123 Binary files /dev/null and b/media/vocal/flore/tetradecacrash_1.ogg differ diff --git a/media/vocal/flore/tridecacrash_1.ogg b/media/vocal/flore/tridecacrash_1.ogg new file mode 100644 index 000000000..8dd702d5f Binary files /dev/null and b/media/vocal/flore/tridecacrash_1.ogg differ diff --git a/media/vocal/flore/triple_1.ogg b/media/vocal/flore/triple_1.ogg new file mode 100644 index 000000000..d8c99d6fc Binary files /dev/null and b/media/vocal/flore/triple_1.ogg differ diff --git a/media/vocal/flore/triple_2.ogg b/media/vocal/flore/triple_2.ogg new file mode 100644 index 000000000..45b349e1c Binary files /dev/null and b/media/vocal/flore/triple_2.ogg differ diff --git a/media/vocal/flore/triple_3.ogg b/media/vocal/flore/triple_3.ogg new file mode 100644 index 000000000..42756d34f Binary files /dev/null and b/media/vocal/flore/triple_3.ogg differ diff --git a/media/vocal/flore/tspin_1.ogg b/media/vocal/flore/tspin_1.ogg new file mode 100644 index 000000000..95f65ce47 Binary files /dev/null and b/media/vocal/flore/tspin_1.ogg differ diff --git a/media/vocal/flore/tspin_2.ogg b/media/vocal/flore/tspin_2.ogg new file mode 100644 index 000000000..d2a9919e3 Binary files /dev/null and b/media/vocal/flore/tspin_2.ogg differ diff --git a/media/vocal/flore/ultracrash_1.ogg b/media/vocal/flore/ultracrash_1.ogg new file mode 100644 index 000000000..172163432 Binary files /dev/null and b/media/vocal/flore/ultracrash_1.ogg differ diff --git a/media/vocal/flore/undecacrash_1.ogg b/media/vocal/flore/undecacrash_1.ogg new file mode 100644 index 000000000..9d7aab013 Binary files /dev/null and b/media/vocal/flore/undecacrash_1.ogg differ diff --git a/media/vocal/flore/uspin.ogg b/media/vocal/flore/uspin.ogg new file mode 100644 index 000000000..cea69dd96 Binary files /dev/null and b/media/vocal/flore/uspin.ogg differ diff --git a/media/vocal/flore/vspin.ogg b/media/vocal/flore/vspin.ogg new file mode 100644 index 000000000..f20235df1 Binary files /dev/null and b/media/vocal/flore/vspin.ogg differ diff --git a/media/vocal/flore/welcome_1.ogg b/media/vocal/flore/welcome_1.ogg new file mode 100644 index 000000000..28dd481a5 Binary files /dev/null and b/media/vocal/flore/welcome_1.ogg differ diff --git a/media/vocal/flore/welcome_2.ogg b/media/vocal/flore/welcome_2.ogg new file mode 100644 index 000000000..a1eec9b6e Binary files /dev/null and b/media/vocal/flore/welcome_2.ogg differ diff --git a/media/vocal/flore/welcome_3.ogg b/media/vocal/flore/welcome_3.ogg new file mode 100644 index 000000000..9093d4787 Binary files /dev/null and b/media/vocal/flore/welcome_3.ogg differ diff --git a/media/vocal/flore/welcome_4.ogg b/media/vocal/flore/welcome_4.ogg new file mode 100644 index 000000000..4da82cd5b Binary files /dev/null and b/media/vocal/flore/welcome_4.ogg differ diff --git a/media/vocal/flore/welcome_5.ogg b/media/vocal/flore/welcome_5.ogg new file mode 100644 index 000000000..795c3b034 Binary files /dev/null and b/media/vocal/flore/welcome_5.ogg differ diff --git a/media/vocal/flore/welcome_6.ogg b/media/vocal/flore/welcome_6.ogg new file mode 100644 index 000000000..90921af24 Binary files /dev/null and b/media/vocal/flore/welcome_6.ogg differ diff --git a/media/vocal/flore/win_1.ogg b/media/vocal/flore/win_1.ogg new file mode 100644 index 000000000..574f33a3f Binary files /dev/null and b/media/vocal/flore/win_1.ogg differ diff --git a/media/vocal/flore/win_2.ogg b/media/vocal/flore/win_2.ogg new file mode 100644 index 000000000..aa2c8a0fd Binary files /dev/null and b/media/vocal/flore/win_2.ogg differ diff --git a/media/vocal/flore/win_3.ogg b/media/vocal/flore/win_3.ogg new file mode 100644 index 000000000..336e321a3 Binary files /dev/null and b/media/vocal/flore/win_3.ogg differ diff --git a/media/vocal/flore/win_4.ogg b/media/vocal/flore/win_4.ogg new file mode 100644 index 000000000..5c4db48b1 Binary files /dev/null and b/media/vocal/flore/win_4.ogg differ diff --git a/media/vocal/flore/win_5.ogg b/media/vocal/flore/win_5.ogg new file mode 100644 index 000000000..c5be6be1a Binary files /dev/null and b/media/vocal/flore/win_5.ogg differ diff --git a/media/vocal/flore/win_6.ogg b/media/vocal/flore/win_6.ogg new file mode 100644 index 000000000..d58806c45 Binary files /dev/null and b/media/vocal/flore/win_6.ogg differ diff --git a/media/vocal/flore/wspin.ogg b/media/vocal/flore/wspin.ogg new file mode 100644 index 000000000..ccf3a7505 Binary files /dev/null and b/media/vocal/flore/wspin.ogg differ diff --git a/media/vocal/flore/xspin.ogg b/media/vocal/flore/xspin.ogg new file mode 100644 index 000000000..c3ef14e8e Binary files /dev/null and b/media/vocal/flore/xspin.ogg differ diff --git a/media/vocal/flore/yspin.ogg b/media/vocal/flore/yspin.ogg new file mode 100644 index 000000000..11e0c91f6 Binary files /dev/null and b/media/vocal/flore/yspin.ogg differ diff --git a/media/vocal/flore/zspin_1.ogg b/media/vocal/flore/zspin_1.ogg new file mode 100644 index 000000000..8c75a5414 Binary files /dev/null and b/media/vocal/flore/zspin_1.ogg differ diff --git a/media/vocal/flore/zspin_2.ogg b/media/vocal/flore/zspin_2.ogg new file mode 100644 index 000000000..90ed0f6d7 Binary files /dev/null and b/media/vocal/flore/zspin_2.ogg differ diff --git a/media/vocal/miya/angry_1.ogg b/media/vocal/miya/angry_1.ogg new file mode 100644 index 000000000..d07a20de0 Binary files /dev/null and b/media/vocal/miya/angry_1.ogg differ diff --git a/media/vocal/miya/b2b_1.ogg b/media/vocal/miya/b2b_1.ogg index 9825507a8..5e8219dc4 100644 Binary files a/media/vocal/miya/b2b_1.ogg and b/media/vocal/miya/b2b_1.ogg differ diff --git a/media/vocal/miya/b2b_2.ogg b/media/vocal/miya/b2b_2.ogg index fe4c83b87..982de8373 100644 Binary files a/media/vocal/miya/b2b_2.ogg and b/media/vocal/miya/b2b_2.ogg differ diff --git a/media/vocal/miya/b2b_3.ogg b/media/vocal/miya/b2b_3.ogg index dca284549..4f44531c9 100644 Binary files a/media/vocal/miya/b2b_3.ogg and b/media/vocal/miya/b2b_3.ogg differ diff --git a/media/vocal/miya/b3b_1.ogg b/media/vocal/miya/b3b_1.ogg index 533febbe8..323d40bbd 100644 Binary files a/media/vocal/miya/b3b_1.ogg and b/media/vocal/miya/b3b_1.ogg differ diff --git a/media/vocal/miya/b3b_2.ogg b/media/vocal/miya/b3b_2.ogg index 0dc6bcc6c..714979420 100644 Binary files a/media/vocal/miya/b3b_2.ogg and b/media/vocal/miya/b3b_2.ogg differ diff --git a/media/vocal/miya/b3b_3.ogg b/media/vocal/miya/b3b_3.ogg new file mode 100644 index 000000000..5d9aff5e9 Binary files /dev/null and b/media/vocal/miya/b3b_3.ogg differ diff --git a/media/vocal/miya/bye_1.ogg b/media/vocal/miya/bye_1.ogg index 13366f0c3..20d1d9353 100644 Binary files a/media/vocal/miya/bye_1.ogg and b/media/vocal/miya/bye_1.ogg differ diff --git a/media/vocal/miya/bye_2.ogg b/media/vocal/miya/bye_2.ogg index 045a26f85..5a77ac21c 100644 Binary files a/media/vocal/miya/bye_2.ogg and b/media/vocal/miya/bye_2.ogg differ diff --git a/media/vocal/miya/bye_3.ogg b/media/vocal/miya/bye_3.ogg new file mode 100644 index 000000000..f20572a47 Binary files /dev/null and b/media/vocal/miya/bye_3.ogg differ diff --git a/media/vocal/miya/bye_4.ogg b/media/vocal/miya/bye_4.ogg new file mode 100644 index 000000000..4445d7494 Binary files /dev/null and b/media/vocal/miya/bye_4.ogg differ diff --git a/media/vocal/miya/bye_5.ogg b/media/vocal/miya/bye_5.ogg new file mode 100644 index 000000000..419c8d60d Binary files /dev/null and b/media/vocal/miya/bye_5.ogg differ diff --git a/media/vocal/miya/clear_1.ogg b/media/vocal/miya/clear_1.ogg deleted file mode 100644 index 3c3ad0758..000000000 Binary files a/media/vocal/miya/clear_1.ogg and /dev/null differ diff --git a/media/vocal/miya/clear_2.ogg b/media/vocal/miya/clear_2.ogg deleted file mode 100644 index 84cc57fa8..000000000 Binary files a/media/vocal/miya/clear_2.ogg and /dev/null differ diff --git a/media/vocal/miya/double_1.ogg b/media/vocal/miya/double_1.ogg index af2b2c634..e76f0f3df 100644 Binary files a/media/vocal/miya/double_1.ogg and b/media/vocal/miya/double_1.ogg differ diff --git a/media/vocal/miya/double_2.ogg b/media/vocal/miya/double_2.ogg index ca10bc118..627183de7 100644 Binary files a/media/vocal/miya/double_2.ogg and b/media/vocal/miya/double_2.ogg differ diff --git a/media/vocal/miya/double_3.ogg b/media/vocal/miya/double_3.ogg index 5e647be72..fb29529db 100644 Binary files a/media/vocal/miya/double_3.ogg and b/media/vocal/miya/double_3.ogg differ diff --git a/media/vocal/miya/double_4.ogg b/media/vocal/miya/double_4.ogg deleted file mode 100644 index 6e2b9b63a..000000000 Binary files a/media/vocal/miya/double_4.ogg and /dev/null differ diff --git a/media/vocal/miya/double_5.ogg b/media/vocal/miya/double_5.ogg deleted file mode 100644 index 950bb06d6..000000000 Binary files a/media/vocal/miya/double_5.ogg and /dev/null differ diff --git a/media/vocal/miya/doubt_1.ogg b/media/vocal/miya/doubt_1.ogg index e0d94c5f4..12af3f355 100644 Binary files a/media/vocal/miya/doubt_1.ogg and b/media/vocal/miya/doubt_1.ogg differ diff --git a/media/vocal/miya/doubt_2.ogg b/media/vocal/miya/doubt_2.ogg index cd7ee9bdc..230e51e85 100644 Binary files a/media/vocal/miya/doubt_2.ogg and b/media/vocal/miya/doubt_2.ogg differ diff --git a/media/vocal/miya/doubt_3.ogg b/media/vocal/miya/doubt_3.ogg new file mode 100644 index 000000000..6895441e0 Binary files /dev/null and b/media/vocal/miya/doubt_3.ogg differ diff --git a/media/vocal/miya/doubt_4.ogg b/media/vocal/miya/doubt_4.ogg new file mode 100644 index 000000000..e3b3d6c2b Binary files /dev/null and b/media/vocal/miya/doubt_4.ogg differ diff --git a/media/vocal/miya/espin_1.ogg b/media/vocal/miya/espin_1.ogg new file mode 100644 index 000000000..ad3f5ba02 Binary files /dev/null and b/media/vocal/miya/espin_1.ogg differ diff --git a/media/vocal/miya/fspin_1.ogg b/media/vocal/miya/fspin_1.ogg new file mode 100644 index 000000000..34d813845 Binary files /dev/null and b/media/vocal/miya/fspin_1.ogg differ diff --git a/media/vocal/miya/half_clear_1.ogg b/media/vocal/miya/half_clear_1.ogg index 43bb3b7fe..5d5f9cd68 100644 Binary files a/media/vocal/miya/half_clear_1.ogg and b/media/vocal/miya/half_clear_1.ogg differ diff --git a/media/vocal/miya/half_clear_2.ogg b/media/vocal/miya/half_clear_2.ogg index ee1476e83..97262b0f8 100644 Binary files a/media/vocal/miya/half_clear_2.ogg and b/media/vocal/miya/half_clear_2.ogg differ diff --git a/media/vocal/miya/happy_1.ogg b/media/vocal/miya/happy_1.ogg index 8b10ba459..8975f100e 100644 Binary files a/media/vocal/miya/happy_1.ogg and b/media/vocal/miya/happy_1.ogg differ diff --git a/media/vocal/miya/happy_2.ogg b/media/vocal/miya/happy_2.ogg index 913ca1188..f16f84cb4 100644 Binary files a/media/vocal/miya/happy_2.ogg and b/media/vocal/miya/happy_2.ogg differ diff --git a/media/vocal/miya/happy_3.ogg b/media/vocal/miya/happy_3.ogg index f9f534965..6114b5ed5 100644 Binary files a/media/vocal/miya/happy_3.ogg and b/media/vocal/miya/happy_3.ogg differ diff --git a/media/vocal/miya/happy_4.ogg b/media/vocal/miya/happy_4.ogg index e1faf5055..38d4b96de 100644 Binary files a/media/vocal/miya/happy_4.ogg and b/media/vocal/miya/happy_4.ogg differ diff --git a/media/vocal/miya/happy_5.ogg b/media/vocal/miya/happy_5.ogg new file mode 100644 index 000000000..bdaebf74c Binary files /dev/null and b/media/vocal/miya/happy_5.ogg differ diff --git a/media/vocal/miya/happy_6.ogg b/media/vocal/miya/happy_6.ogg new file mode 100644 index 000000000..b1c09ef0e Binary files /dev/null and b/media/vocal/miya/happy_6.ogg differ diff --git a/media/vocal/miya/happy_7.ogg b/media/vocal/miya/happy_7.ogg new file mode 100644 index 000000000..63afff9c3 Binary files /dev/null and b/media/vocal/miya/happy_7.ogg differ diff --git a/media/vocal/miya/hspin_1.ogg b/media/vocal/miya/hspin_1.ogg new file mode 100644 index 000000000..3279d8946 Binary files /dev/null and b/media/vocal/miya/hspin_1.ogg differ diff --git a/media/vocal/miya/ispin_1.ogg b/media/vocal/miya/ispin_1.ogg index 25a5539f6..55e2ef080 100644 Binary files a/media/vocal/miya/ispin_1.ogg and b/media/vocal/miya/ispin_1.ogg differ diff --git a/media/vocal/miya/ispin_2.ogg b/media/vocal/miya/ispin_2.ogg index 661ea38be..a337b8aa2 100644 Binary files a/media/vocal/miya/ispin_2.ogg and b/media/vocal/miya/ispin_2.ogg differ diff --git a/media/vocal/miya/ispin_3.ogg b/media/vocal/miya/ispin_3.ogg deleted file mode 100644 index b0233fa7c..000000000 Binary files a/media/vocal/miya/ispin_3.ogg and /dev/null differ diff --git a/media/vocal/miya/jspin_1.ogg b/media/vocal/miya/jspin_1.ogg index efe109356..6d0c7a410 100644 Binary files a/media/vocal/miya/jspin_1.ogg and b/media/vocal/miya/jspin_1.ogg differ diff --git a/media/vocal/miya/jspin_2.ogg b/media/vocal/miya/jspin_2.ogg index 81abf3bc9..6b362600a 100644 Binary files a/media/vocal/miya/jspin_2.ogg and b/media/vocal/miya/jspin_2.ogg differ diff --git a/media/vocal/miya/jspin_3.ogg b/media/vocal/miya/jspin_3.ogg deleted file mode 100644 index cb9c44e87..000000000 Binary files a/media/vocal/miya/jspin_3.ogg and /dev/null differ diff --git a/media/vocal/miya/jspin_4.ogg b/media/vocal/miya/jspin_4.ogg deleted file mode 100644 index 3b6eb33c0..000000000 Binary files a/media/vocal/miya/jspin_4.ogg and /dev/null differ diff --git a/media/vocal/miya/lose_1.ogg b/media/vocal/miya/lose_1.ogg index 0d6c2e70c..baaa6d8a0 100644 Binary files a/media/vocal/miya/lose_1.ogg and b/media/vocal/miya/lose_1.ogg differ diff --git a/media/vocal/miya/lose_2.ogg b/media/vocal/miya/lose_2.ogg index 29ebd17b1..6b97a663d 100644 Binary files a/media/vocal/miya/lose_2.ogg and b/media/vocal/miya/lose_2.ogg differ diff --git a/media/vocal/miya/lose_3.ogg b/media/vocal/miya/lose_3.ogg index da48ed4bb..a590b5799 100644 Binary files a/media/vocal/miya/lose_3.ogg and b/media/vocal/miya/lose_3.ogg differ diff --git a/media/vocal/miya/lose_4.ogg b/media/vocal/miya/lose_4.ogg new file mode 100644 index 000000000..bb724ee72 Binary files /dev/null and b/media/vocal/miya/lose_4.ogg differ diff --git a/media/vocal/miya/lose_5.ogg b/media/vocal/miya/lose_5.ogg new file mode 100644 index 000000000..2f7394ee5 Binary files /dev/null and b/media/vocal/miya/lose_5.ogg differ diff --git a/media/vocal/miya/lose_6.ogg b/media/vocal/miya/lose_6.ogg new file mode 100644 index 000000000..cf5dacb81 Binary files /dev/null and b/media/vocal/miya/lose_6.ogg differ diff --git a/media/vocal/miya/lspin_1.ogg b/media/vocal/miya/lspin_1.ogg index ff0342f25..5af71baac 100644 Binary files a/media/vocal/miya/lspin_1.ogg and b/media/vocal/miya/lspin_1.ogg differ diff --git a/media/vocal/miya/lspin_2.ogg b/media/vocal/miya/lspin_2.ogg deleted file mode 100644 index 102abe279..000000000 Binary files a/media/vocal/miya/lspin_2.ogg and /dev/null differ diff --git a/media/vocal/miya/mini_1.ogg b/media/vocal/miya/mini_1.ogg index 8678bc2eb..f4586fbc6 100644 Binary files a/media/vocal/miya/mini_1.ogg and b/media/vocal/miya/mini_1.ogg differ diff --git a/media/vocal/miya/mini_2.ogg b/media/vocal/miya/mini_2.ogg index 4b1230a4e..4953c597b 100644 Binary files a/media/vocal/miya/mini_2.ogg and b/media/vocal/miya/mini_2.ogg differ diff --git a/media/vocal/miya/mini_3.ogg b/media/vocal/miya/mini_3.ogg index cf10f2291..bde5b757d 100644 Binary files a/media/vocal/miya/mini_3.ogg and b/media/vocal/miya/mini_3.ogg differ diff --git a/media/vocal/miya/mini_4.ogg b/media/vocal/miya/mini_4.ogg new file mode 100644 index 000000000..c68945bc5 Binary files /dev/null and b/media/vocal/miya/mini_4.ogg differ diff --git a/media/vocal/miya/mini_5.ogg b/media/vocal/miya/mini_5.ogg new file mode 100644 index 000000000..2d3037502 Binary files /dev/null and b/media/vocal/miya/mini_5.ogg differ diff --git a/media/vocal/miya/nspin_1.ogg b/media/vocal/miya/nspin_1.ogg new file mode 100644 index 000000000..cacb9d011 Binary files /dev/null and b/media/vocal/miya/nspin_1.ogg differ diff --git a/media/vocal/miya/nya_1.ogg b/media/vocal/miya/nya_1.ogg deleted file mode 100644 index c51b00c5e..000000000 Binary files a/media/vocal/miya/nya_1.ogg and /dev/null differ diff --git a/media/vocal/miya/nya_2.ogg b/media/vocal/miya/nya_2.ogg deleted file mode 100644 index e278ffffa..000000000 Binary files a/media/vocal/miya/nya_2.ogg and /dev/null differ diff --git a/media/vocal/miya/nya_3.ogg b/media/vocal/miya/nya_3.ogg deleted file mode 100644 index 0ed6aad7e..000000000 Binary files a/media/vocal/miya/nya_3.ogg and /dev/null differ diff --git a/media/vocal/miya/nya_4.ogg b/media/vocal/miya/nya_4.ogg deleted file mode 100644 index 9a7307c86..000000000 Binary files a/media/vocal/miya/nya_4.ogg and /dev/null differ diff --git a/media/vocal/miya/nya_doubt_1.ogg b/media/vocal/miya/nya_doubt_1.ogg deleted file mode 100644 index e0d94c5f4..000000000 Binary files a/media/vocal/miya/nya_doubt_1.ogg and /dev/null differ diff --git a/media/vocal/miya/nya_doubt_2.ogg b/media/vocal/miya/nya_doubt_2.ogg deleted file mode 100644 index cd7ee9bdc..000000000 Binary files a/media/vocal/miya/nya_doubt_2.ogg and /dev/null differ diff --git a/media/vocal/miya/nya_happy_1.ogg b/media/vocal/miya/nya_happy_1.ogg deleted file mode 100644 index 8b10ba459..000000000 Binary files a/media/vocal/miya/nya_happy_1.ogg and /dev/null differ diff --git a/media/vocal/miya/nya_happy_2.ogg b/media/vocal/miya/nya_happy_2.ogg deleted file mode 100644 index 913ca1188..000000000 Binary files a/media/vocal/miya/nya_happy_2.ogg and /dev/null differ diff --git a/media/vocal/miya/nya_happy_3.ogg b/media/vocal/miya/nya_happy_3.ogg deleted file mode 100644 index f9f534965..000000000 Binary files a/media/vocal/miya/nya_happy_3.ogg and /dev/null differ diff --git a/media/vocal/miya/nya_happy_4.ogg b/media/vocal/miya/nya_happy_4.ogg deleted file mode 100644 index e1faf5055..000000000 Binary files a/media/vocal/miya/nya_happy_4.ogg and /dev/null differ diff --git a/media/vocal/miya/nya_sad_1.ogg b/media/vocal/miya/nya_sad_1.ogg deleted file mode 100644 index 4270f18ca..000000000 Binary files a/media/vocal/miya/nya_sad_1.ogg and /dev/null differ diff --git a/media/vocal/miya/ospin_1.ogg b/media/vocal/miya/ospin_1.ogg index 1d0651ed3..26ad0e6ef 100644 Binary files a/media/vocal/miya/ospin_1.ogg and b/media/vocal/miya/ospin_1.ogg differ diff --git a/media/vocal/miya/ospin_2.ogg b/media/vocal/miya/ospin_2.ogg index a70f0e173..4fa006ab9 100644 Binary files a/media/vocal/miya/ospin_2.ogg and b/media/vocal/miya/ospin_2.ogg differ diff --git a/media/vocal/miya/ospin_3.ogg b/media/vocal/miya/ospin_3.ogg deleted file mode 100644 index b8523892b..000000000 Binary files a/media/vocal/miya/ospin_3.ogg and /dev/null differ diff --git a/media/vocal/miya/perfect_clear_1.ogg b/media/vocal/miya/perfect_clear_1.ogg index 3c3ad0758..aaf235f32 100644 Binary files a/media/vocal/miya/perfect_clear_1.ogg and b/media/vocal/miya/perfect_clear_1.ogg differ diff --git a/media/vocal/miya/perfect_clear_2.ogg b/media/vocal/miya/perfect_clear_2.ogg index 84cc57fa8..9efdadfaa 100644 Binary files a/media/vocal/miya/perfect_clear_2.ogg and b/media/vocal/miya/perfect_clear_2.ogg differ diff --git a/media/vocal/miya/perfect_clear_3.ogg b/media/vocal/miya/perfect_clear_3.ogg new file mode 100644 index 000000000..68b610f0a Binary files /dev/null and b/media/vocal/miya/perfect_clear_3.ogg differ diff --git a/media/vocal/miya/perfect_clear_4.ogg b/media/vocal/miya/perfect_clear_4.ogg new file mode 100644 index 000000000..0a165b671 Binary files /dev/null and b/media/vocal/miya/perfect_clear_4.ogg differ diff --git a/media/vocal/miya/perfect_clear_5.ogg b/media/vocal/miya/perfect_clear_5.ogg new file mode 100644 index 000000000..91579e50f Binary files /dev/null and b/media/vocal/miya/perfect_clear_5.ogg differ diff --git a/media/vocal/miya/pspin_1.ogg b/media/vocal/miya/pspin_1.ogg new file mode 100644 index 000000000..10bb18108 Binary files /dev/null and b/media/vocal/miya/pspin_1.ogg differ diff --git a/media/vocal/miya/qspin_1.ogg b/media/vocal/miya/qspin_1.ogg new file mode 100644 index 000000000..7acff5135 Binary files /dev/null and b/media/vocal/miya/qspin_1.ogg differ diff --git a/media/vocal/miya/rspin_1.ogg b/media/vocal/miya/rspin_1.ogg new file mode 100644 index 000000000..69cddb03a Binary files /dev/null and b/media/vocal/miya/rspin_1.ogg differ diff --git a/media/vocal/miya/sad_1.ogg b/media/vocal/miya/sad_1.ogg new file mode 100644 index 000000000..9654ff7b1 Binary files /dev/null and b/media/vocal/miya/sad_1.ogg differ diff --git a/media/vocal/miya/single_1.ogg b/media/vocal/miya/single_1.ogg index 41ec27983..41f9411d0 100644 Binary files a/media/vocal/miya/single_1.ogg and b/media/vocal/miya/single_1.ogg differ diff --git a/media/vocal/miya/single_2.ogg b/media/vocal/miya/single_2.ogg index e74c71030..cf32a878f 100644 Binary files a/media/vocal/miya/single_2.ogg and b/media/vocal/miya/single_2.ogg differ diff --git a/media/vocal/miya/single_3.ogg b/media/vocal/miya/single_3.ogg deleted file mode 100644 index b90251aff..000000000 Binary files a/media/vocal/miya/single_3.ogg and /dev/null differ diff --git a/media/vocal/miya/single_4.ogg b/media/vocal/miya/single_4.ogg deleted file mode 100644 index d66efcf7e..000000000 Binary files a/media/vocal/miya/single_4.ogg and /dev/null differ diff --git a/media/vocal/miya/single_5.ogg b/media/vocal/miya/single_5.ogg deleted file mode 100644 index 43c718659..000000000 Binary files a/media/vocal/miya/single_5.ogg and /dev/null differ diff --git a/media/vocal/miya/single_6.ogg b/media/vocal/miya/single_6.ogg deleted file mode 100644 index edf427ad4..000000000 Binary files a/media/vocal/miya/single_6.ogg and /dev/null differ diff --git a/media/vocal/miya/single_7.ogg b/media/vocal/miya/single_7.ogg deleted file mode 100644 index f173c2dfc..000000000 Binary files a/media/vocal/miya/single_7.ogg and /dev/null differ diff --git a/media/vocal/miya/sspin_1.ogg b/media/vocal/miya/sspin_1.ogg index b620a7e0d..34347314c 100644 Binary files a/media/vocal/miya/sspin_1.ogg and b/media/vocal/miya/sspin_1.ogg differ diff --git a/media/vocal/miya/sspin_2.ogg b/media/vocal/miya/sspin_2.ogg index e32904b5f..565df4836 100644 Binary files a/media/vocal/miya/sspin_2.ogg and b/media/vocal/miya/sspin_2.ogg differ diff --git a/media/vocal/miya/sspin_3.ogg b/media/vocal/miya/sspin_3.ogg index e6cb63311..49baebd8b 100644 Binary files a/media/vocal/miya/sspin_3.ogg and b/media/vocal/miya/sspin_3.ogg differ diff --git a/media/vocal/miya/sspin_4.ogg b/media/vocal/miya/sspin_4.ogg index a1f184688..93ad383f3 100644 Binary files a/media/vocal/miya/sspin_4.ogg and b/media/vocal/miya/sspin_4.ogg differ diff --git a/media/vocal/miya/sspin_5.ogg b/media/vocal/miya/sspin_5.ogg deleted file mode 100644 index 2494d3bb5..000000000 Binary files a/media/vocal/miya/sspin_5.ogg and /dev/null differ diff --git a/media/vocal/miya/sspin_6.ogg b/media/vocal/miya/sspin_6.ogg deleted file mode 100644 index e2690ea5c..000000000 Binary files a/media/vocal/miya/sspin_6.ogg and /dev/null differ diff --git a/media/vocal/miya/techrash_1.ogg b/media/vocal/miya/techrash_1.ogg index 4c4b6f146..32347e031 100644 Binary files a/media/vocal/miya/techrash_1.ogg and b/media/vocal/miya/techrash_1.ogg differ diff --git a/media/vocal/miya/techrash_2.ogg b/media/vocal/miya/techrash_2.ogg index c56a01f1b..b45e0783b 100644 Binary files a/media/vocal/miya/techrash_2.ogg and b/media/vocal/miya/techrash_2.ogg differ diff --git a/media/vocal/miya/techrash_3.ogg b/media/vocal/miya/techrash_3.ogg deleted file mode 100644 index 49bc7ab0f..000000000 Binary files a/media/vocal/miya/techrash_3.ogg and /dev/null differ diff --git a/media/vocal/miya/techrash_4.ogg b/media/vocal/miya/techrash_4.ogg deleted file mode 100644 index c001c793f..000000000 Binary files a/media/vocal/miya/techrash_4.ogg and /dev/null differ diff --git a/media/vocal/miya/test_1.ogg b/media/vocal/miya/test_1.ogg index c51b00c5e..68724ef42 100644 Binary files a/media/vocal/miya/test_1.ogg and b/media/vocal/miya/test_1.ogg differ diff --git a/media/vocal/miya/test_2.ogg b/media/vocal/miya/test_2.ogg index e278ffffa..6484f0c9a 100644 Binary files a/media/vocal/miya/test_2.ogg and b/media/vocal/miya/test_2.ogg differ diff --git a/media/vocal/miya/test_3.ogg b/media/vocal/miya/test_3.ogg index 0ed6aad7e..f0d6d41a1 100644 Binary files a/media/vocal/miya/test_3.ogg and b/media/vocal/miya/test_3.ogg differ diff --git a/media/vocal/miya/test_4.ogg b/media/vocal/miya/test_4.ogg index 9a7307c86..e33d10c77 100644 Binary files a/media/vocal/miya/test_4.ogg and b/media/vocal/miya/test_4.ogg differ diff --git a/media/vocal/miya/triple_1.ogg b/media/vocal/miya/triple_1.ogg index 7d564188b..aaf713710 100644 Binary files a/media/vocal/miya/triple_1.ogg and b/media/vocal/miya/triple_1.ogg differ diff --git a/media/vocal/miya/triple_2.ogg b/media/vocal/miya/triple_2.ogg index 58518f144..7285cb6af 100644 Binary files a/media/vocal/miya/triple_2.ogg and b/media/vocal/miya/triple_2.ogg differ diff --git a/media/vocal/miya/triple_3.ogg b/media/vocal/miya/triple_3.ogg index eb9daf3b9..19a697966 100644 Binary files a/media/vocal/miya/triple_3.ogg and b/media/vocal/miya/triple_3.ogg differ diff --git a/media/vocal/miya/triple_4.ogg b/media/vocal/miya/triple_4.ogg index cea64b4ab..6d10a504f 100644 Binary files a/media/vocal/miya/triple_4.ogg and b/media/vocal/miya/triple_4.ogg differ diff --git a/media/vocal/miya/triple_5.ogg b/media/vocal/miya/triple_5.ogg deleted file mode 100644 index b19062fc0..000000000 Binary files a/media/vocal/miya/triple_5.ogg and /dev/null differ diff --git a/media/vocal/miya/triple_6.ogg b/media/vocal/miya/triple_6.ogg deleted file mode 100644 index 0221d8437..000000000 Binary files a/media/vocal/miya/triple_6.ogg and /dev/null differ diff --git a/media/vocal/miya/triple_7.ogg b/media/vocal/miya/triple_7.ogg deleted file mode 100644 index 4dedec5b9..000000000 Binary files a/media/vocal/miya/triple_7.ogg and /dev/null differ diff --git a/media/vocal/miya/tspin_1.ogg b/media/vocal/miya/tspin_1.ogg index 982712b17..c70038844 100644 Binary files a/media/vocal/miya/tspin_1.ogg and b/media/vocal/miya/tspin_1.ogg differ diff --git a/media/vocal/miya/tspin_2.ogg b/media/vocal/miya/tspin_2.ogg index f597536ad..87640b3a6 100644 Binary files a/media/vocal/miya/tspin_2.ogg and b/media/vocal/miya/tspin_2.ogg differ diff --git a/media/vocal/miya/tspin_3.ogg b/media/vocal/miya/tspin_3.ogg index d15c53480..6a1a8a422 100644 Binary files a/media/vocal/miya/tspin_3.ogg and b/media/vocal/miya/tspin_3.ogg differ diff --git a/media/vocal/miya/tspin_4.ogg b/media/vocal/miya/tspin_4.ogg deleted file mode 100644 index 3747c6eb8..000000000 Binary files a/media/vocal/miya/tspin_4.ogg and /dev/null differ diff --git a/media/vocal/miya/tspin_5.ogg b/media/vocal/miya/tspin_5.ogg deleted file mode 100644 index af2487ea0..000000000 Binary files a/media/vocal/miya/tspin_5.ogg and /dev/null differ diff --git a/media/vocal/miya/tspin_6.ogg b/media/vocal/miya/tspin_6.ogg deleted file mode 100644 index 0b569d33e..000000000 Binary files a/media/vocal/miya/tspin_6.ogg and /dev/null differ diff --git a/media/vocal/miya/uspin_1.ogg b/media/vocal/miya/uspin_1.ogg new file mode 100644 index 000000000..3ed149243 Binary files /dev/null and b/media/vocal/miya/uspin_1.ogg differ diff --git a/media/vocal/miya/vspin_1.ogg b/media/vocal/miya/vspin_1.ogg new file mode 100644 index 000000000..2b1c5f2bf Binary files /dev/null and b/media/vocal/miya/vspin_1.ogg differ diff --git a/media/vocal/miya/welcome.ogg b/media/vocal/miya/welcome.ogg deleted file mode 100644 index 908560473..000000000 Binary files a/media/vocal/miya/welcome.ogg and /dev/null differ diff --git a/media/vocal/miya/welcome_1.ogg b/media/vocal/miya/welcome_1.ogg new file mode 100644 index 000000000..11e74486c Binary files /dev/null and b/media/vocal/miya/welcome_1.ogg differ diff --git a/media/vocal/miya/welcome_2.ogg b/media/vocal/miya/welcome_2.ogg new file mode 100644 index 000000000..daf001cce Binary files /dev/null and b/media/vocal/miya/welcome_2.ogg differ diff --git a/media/vocal/miya/welcome_3.ogg b/media/vocal/miya/welcome_3.ogg new file mode 100644 index 000000000..1b8b77499 Binary files /dev/null and b/media/vocal/miya/welcome_3.ogg differ diff --git a/media/vocal/miya/welcome_4.ogg b/media/vocal/miya/welcome_4.ogg new file mode 100644 index 000000000..33df5771e Binary files /dev/null and b/media/vocal/miya/welcome_4.ogg differ diff --git a/media/vocal/miya/welcome_5.ogg b/media/vocal/miya/welcome_5.ogg new file mode 100644 index 000000000..5f85a97b2 Binary files /dev/null and b/media/vocal/miya/welcome_5.ogg differ diff --git a/media/vocal/miya/welcome_6.ogg b/media/vocal/miya/welcome_6.ogg new file mode 100644 index 000000000..46706bfb3 Binary files /dev/null and b/media/vocal/miya/welcome_6.ogg differ diff --git a/media/vocal/miya/win_1.ogg b/media/vocal/miya/win_1.ogg index 502d5d22b..0811560a3 100644 Binary files a/media/vocal/miya/win_1.ogg and b/media/vocal/miya/win_1.ogg differ diff --git a/media/vocal/miya/win_2.ogg b/media/vocal/miya/win_2.ogg index 543ca30ac..18d2912b6 100644 Binary files a/media/vocal/miya/win_2.ogg and b/media/vocal/miya/win_2.ogg differ diff --git a/media/vocal/miya/win_3.ogg b/media/vocal/miya/win_3.ogg index f50557127..86861684d 100644 Binary files a/media/vocal/miya/win_3.ogg and b/media/vocal/miya/win_3.ogg differ diff --git a/media/vocal/miya/win_4.ogg b/media/vocal/miya/win_4.ogg index 7528d3929..bb55c76b6 100644 Binary files a/media/vocal/miya/win_4.ogg and b/media/vocal/miya/win_4.ogg differ diff --git a/media/vocal/miya/win_5.ogg b/media/vocal/miya/win_5.ogg index c0e14db01..8210e03c1 100644 Binary files a/media/vocal/miya/win_5.ogg and b/media/vocal/miya/win_5.ogg differ diff --git a/media/vocal/miya/win_6.ogg b/media/vocal/miya/win_6.ogg index 99f681c41..1fe6416be 100644 Binary files a/media/vocal/miya/win_6.ogg and b/media/vocal/miya/win_6.ogg differ diff --git a/media/vocal/miya/win_7.ogg b/media/vocal/miya/win_7.ogg deleted file mode 100644 index fa5fa03be..000000000 Binary files a/media/vocal/miya/win_7.ogg and /dev/null differ diff --git a/media/vocal/miya/win_8.ogg b/media/vocal/miya/win_8.ogg deleted file mode 100644 index bcf18792d..000000000 Binary files a/media/vocal/miya/win_8.ogg and /dev/null differ diff --git a/media/vocal/miya/wspin_1.ogg b/media/vocal/miya/wspin_1.ogg new file mode 100644 index 000000000..3e2d490a5 Binary files /dev/null and b/media/vocal/miya/wspin_1.ogg differ diff --git a/media/vocal/miya/xspin_1.ogg b/media/vocal/miya/xspin_1.ogg new file mode 100644 index 000000000..823707d99 Binary files /dev/null and b/media/vocal/miya/xspin_1.ogg differ diff --git a/media/vocal/miya/yspin_1.ogg b/media/vocal/miya/yspin_1.ogg new file mode 100644 index 000000000..82af8c26d Binary files /dev/null and b/media/vocal/miya/yspin_1.ogg differ diff --git a/media/vocal/miya/zspin_1.ogg b/media/vocal/miya/zspin_1.ogg index 8da82c520..7e9698975 100644 Binary files a/media/vocal/miya/zspin_1.ogg and b/media/vocal/miya/zspin_1.ogg differ diff --git a/media/vocal/miya/zspin_2.ogg b/media/vocal/miya/zspin_2.ogg index 776e51933..42736c160 100644 Binary files a/media/vocal/miya/zspin_2.ogg and b/media/vocal/miya/zspin_2.ogg differ diff --git a/media/vocal/miya/zspin_3.ogg b/media/vocal/miya/zspin_3.ogg index 91bd6caa8..c43fdad05 100644 Binary files a/media/vocal/miya/zspin_3.ogg and b/media/vocal/miya/zspin_3.ogg differ diff --git a/media/vocal/mono/bye_1.ogg b/media/vocal/mono/bye_1.ogg new file mode 100644 index 000000000..973a7fbdc Binary files /dev/null and b/media/vocal/mono/bye_1.ogg differ diff --git a/media/vocal/mono/bye_2.ogg b/media/vocal/mono/bye_2.ogg new file mode 100644 index 000000000..65073d7c3 Binary files /dev/null and b/media/vocal/mono/bye_2.ogg differ diff --git a/media/vocal/mono/bye_3.ogg b/media/vocal/mono/bye_3.ogg new file mode 100644 index 000000000..70c9e3761 Binary files /dev/null and b/media/vocal/mono/bye_3.ogg differ diff --git a/media/vocal/mono/bye_4.ogg b/media/vocal/mono/bye_4.ogg new file mode 100644 index 000000000..24b94a561 Binary files /dev/null and b/media/vocal/mono/bye_4.ogg differ diff --git a/media/vocal/mono/clear_1.ogg b/media/vocal/mono/clear_1.ogg new file mode 100644 index 000000000..9d6714a3c Binary files /dev/null and b/media/vocal/mono/clear_1.ogg differ diff --git a/media/vocal/mono/clear_2.ogg b/media/vocal/mono/clear_2.ogg new file mode 100644 index 000000000..1542fe6e7 Binary files /dev/null and b/media/vocal/mono/clear_2.ogg differ diff --git a/media/vocal/mono/clear_3.ogg b/media/vocal/mono/clear_3.ogg new file mode 100644 index 000000000..fc0bf401c Binary files /dev/null and b/media/vocal/mono/clear_3.ogg differ diff --git a/media/vocal/mono/clear_4.ogg b/media/vocal/mono/clear_4.ogg new file mode 100644 index 000000000..e128d9fc4 Binary files /dev/null and b/media/vocal/mono/clear_4.ogg differ diff --git a/media/vocal/mono/double.ogg b/media/vocal/mono/double.ogg deleted file mode 100644 index 8617370df..000000000 Binary files a/media/vocal/mono/double.ogg and /dev/null differ diff --git a/media/vocal/mono/double_1.ogg b/media/vocal/mono/double_1.ogg new file mode 100644 index 000000000..8469bf0a0 Binary files /dev/null and b/media/vocal/mono/double_1.ogg differ diff --git a/media/vocal/mono/double_2.ogg b/media/vocal/mono/double_2.ogg new file mode 100644 index 000000000..77bbd7009 Binary files /dev/null and b/media/vocal/mono/double_2.ogg differ diff --git a/media/vocal/mono/double_3.ogg b/media/vocal/mono/double_3.ogg new file mode 100644 index 000000000..f13dc6535 Binary files /dev/null and b/media/vocal/mono/double_3.ogg differ diff --git a/media/vocal/mono/double_4.ogg b/media/vocal/mono/double_4.ogg new file mode 100644 index 000000000..e68d0d3af Binary files /dev/null and b/media/vocal/mono/double_4.ogg differ diff --git a/media/vocal/mono/doubt_1.ogg b/media/vocal/mono/doubt_1.ogg new file mode 100644 index 000000000..7cf249862 Binary files /dev/null and b/media/vocal/mono/doubt_1.ogg differ diff --git a/media/vocal/mono/doubt_2.ogg b/media/vocal/mono/doubt_2.ogg new file mode 100644 index 000000000..626a63aea Binary files /dev/null and b/media/vocal/mono/doubt_2.ogg differ diff --git a/media/vocal/mono/doubt_3.ogg b/media/vocal/mono/doubt_3.ogg new file mode 100644 index 000000000..63a37d62b Binary files /dev/null and b/media/vocal/mono/doubt_3.ogg differ diff --git a/media/vocal/mono/doubt_4.ogg b/media/vocal/mono/doubt_4.ogg new file mode 100644 index 000000000..c48cfec60 Binary files /dev/null and b/media/vocal/mono/doubt_4.ogg differ diff --git a/media/vocal/mono/half_clear_1.ogg b/media/vocal/mono/half_clear.ogg similarity index 100% rename from media/vocal/mono/half_clear_1.ogg rename to media/vocal/mono/half_clear.ogg diff --git a/media/vocal/mono/happy_1.ogg b/media/vocal/mono/happy_1.ogg new file mode 100644 index 000000000..55013860b Binary files /dev/null and b/media/vocal/mono/happy_1.ogg differ diff --git a/media/vocal/mono/happy_2.ogg b/media/vocal/mono/happy_2.ogg new file mode 100644 index 000000000..71445d813 Binary files /dev/null and b/media/vocal/mono/happy_2.ogg differ diff --git a/media/vocal/mono/happy_3.ogg b/media/vocal/mono/happy_3.ogg new file mode 100644 index 000000000..b98941221 Binary files /dev/null and b/media/vocal/mono/happy_3.ogg differ diff --git a/media/vocal/mono/happy_4.ogg b/media/vocal/mono/happy_4.ogg new file mode 100644 index 000000000..e076edbb7 Binary files /dev/null and b/media/vocal/mono/happy_4.ogg differ diff --git a/media/vocal/mono/hexacrash_1.ogg b/media/vocal/mono/hexacrash_1.ogg new file mode 100644 index 000000000..8bdf767cb Binary files /dev/null and b/media/vocal/mono/hexacrash_1.ogg differ diff --git a/media/vocal/mono/hexacrash_2.ogg b/media/vocal/mono/hexacrash_2.ogg new file mode 100644 index 000000000..3183fc80e Binary files /dev/null and b/media/vocal/mono/hexacrash_2.ogg differ diff --git a/media/vocal/mono/hexacrash_3.ogg b/media/vocal/mono/hexacrash_3.ogg new file mode 100644 index 000000000..702c2b205 Binary files /dev/null and b/media/vocal/mono/hexacrash_3.ogg differ diff --git a/media/vocal/mono/hexacrash_4.ogg b/media/vocal/mono/hexacrash_4.ogg new file mode 100644 index 000000000..c9b7d7945 Binary files /dev/null and b/media/vocal/mono/hexacrash_4.ogg differ diff --git a/media/vocal/mono/ispin.ogg b/media/vocal/mono/ispin.ogg new file mode 100644 index 000000000..07ef4f09b Binary files /dev/null and b/media/vocal/mono/ispin.ogg differ diff --git a/media/vocal/mono/ispin0.ogg b/media/vocal/mono/ispin0.ogg new file mode 100644 index 000000000..5a9fcea46 Binary files /dev/null and b/media/vocal/mono/ispin0.ogg differ diff --git a/media/vocal/mono/ispin_1.ogg b/media/vocal/mono/ispin_1.ogg deleted file mode 100644 index b269f77c1..000000000 Binary files a/media/vocal/mono/ispin_1.ogg and /dev/null differ diff --git a/media/vocal/mono/ispin_2.ogg b/media/vocal/mono/ispin_2.ogg deleted file mode 100644 index 63e40ee35..000000000 Binary files a/media/vocal/mono/ispin_2.ogg and /dev/null differ diff --git a/media/vocal/mono/ispin_3.ogg b/media/vocal/mono/ispin_3.ogg deleted file mode 100644 index 310ed5791..000000000 Binary files a/media/vocal/mono/ispin_3.ogg and /dev/null differ diff --git a/media/vocal/mono/ispin_4.ogg b/media/vocal/mono/ispin_4.ogg deleted file mode 100644 index bf506a045..000000000 Binary files a/media/vocal/mono/ispin_4.ogg and /dev/null differ diff --git a/media/vocal/mono/jspin.ogg b/media/vocal/mono/jspin.ogg new file mode 100644 index 000000000..579eddd65 Binary files /dev/null and b/media/vocal/mono/jspin.ogg differ diff --git a/media/vocal/mono/jspin0.ogg b/media/vocal/mono/jspin0.ogg new file mode 100644 index 000000000..35d7dda15 Binary files /dev/null and b/media/vocal/mono/jspin0.ogg differ diff --git a/media/vocal/mono/jspin_1.ogg b/media/vocal/mono/jspin_1.ogg deleted file mode 100644 index 420eb5f34..000000000 Binary files a/media/vocal/mono/jspin_1.ogg and /dev/null differ diff --git a/media/vocal/mono/jspin_2.ogg b/media/vocal/mono/jspin_2.ogg deleted file mode 100644 index 68fb6f3dd..000000000 Binary files a/media/vocal/mono/jspin_2.ogg and /dev/null differ diff --git a/media/vocal/mono/jspin_3.ogg b/media/vocal/mono/jspin_3.ogg deleted file mode 100644 index 407052326..000000000 Binary files a/media/vocal/mono/jspin_3.ogg and /dev/null differ diff --git a/media/vocal/mono/jspin_4.ogg b/media/vocal/mono/jspin_4.ogg deleted file mode 100644 index 322556027..000000000 Binary files a/media/vocal/mono/jspin_4.ogg and /dev/null differ diff --git a/media/vocal/mono/lose_1.ogg b/media/vocal/mono/lose_1.ogg index 7cd53e1bb..9b645c3d4 100644 Binary files a/media/vocal/mono/lose_1.ogg and b/media/vocal/mono/lose_1.ogg differ diff --git a/media/vocal/mono/lose_2.ogg b/media/vocal/mono/lose_2.ogg index 58c0f5b38..94fe5124c 100644 Binary files a/media/vocal/mono/lose_2.ogg and b/media/vocal/mono/lose_2.ogg differ diff --git a/media/vocal/mono/lose_3.ogg b/media/vocal/mono/lose_3.ogg index fbab21e72..8ede047a3 100644 Binary files a/media/vocal/mono/lose_3.ogg and b/media/vocal/mono/lose_3.ogg differ diff --git a/media/vocal/mono/lose_4.ogg b/media/vocal/mono/lose_4.ogg new file mode 100644 index 000000000..2832ab42d Binary files /dev/null and b/media/vocal/mono/lose_4.ogg differ diff --git a/media/vocal/mono/lose_5.ogg b/media/vocal/mono/lose_5.ogg new file mode 100644 index 000000000..1b3a2171b Binary files /dev/null and b/media/vocal/mono/lose_5.ogg differ diff --git a/media/vocal/mono/lose_6.ogg b/media/vocal/mono/lose_6.ogg new file mode 100644 index 000000000..181497731 Binary files /dev/null and b/media/vocal/mono/lose_6.ogg differ diff --git a/media/vocal/mono/lspin.ogg b/media/vocal/mono/lspin.ogg new file mode 100644 index 000000000..83097cbc7 Binary files /dev/null and b/media/vocal/mono/lspin.ogg differ diff --git a/media/vocal/mono/lspin0.ogg b/media/vocal/mono/lspin0.ogg new file mode 100644 index 000000000..9ae4cc605 Binary files /dev/null and b/media/vocal/mono/lspin0.ogg differ diff --git a/media/vocal/mono/lspin_1.ogg b/media/vocal/mono/lspin_1.ogg deleted file mode 100644 index ccd114b18..000000000 Binary files a/media/vocal/mono/lspin_1.ogg and /dev/null differ diff --git a/media/vocal/mono/lspin_2.ogg b/media/vocal/mono/lspin_2.ogg deleted file mode 100644 index ac44b0045..000000000 Binary files a/media/vocal/mono/lspin_2.ogg and /dev/null differ diff --git a/media/vocal/mono/lspin_3.ogg b/media/vocal/mono/lspin_3.ogg deleted file mode 100644 index 72995edb7..000000000 Binary files a/media/vocal/mono/lspin_3.ogg and /dev/null differ diff --git a/media/vocal/mono/mini_1.ogg b/media/vocal/mono/mini_1.ogg deleted file mode 100644 index 4a5e4a2e2..000000000 Binary files a/media/vocal/mono/mini_1.ogg and /dev/null differ diff --git a/media/vocal/mono/mini_2.ogg b/media/vocal/mono/mini_2.ogg deleted file mode 100644 index 1245e8a2a..000000000 Binary files a/media/vocal/mono/mini_2.ogg and /dev/null differ diff --git a/media/vocal/mono/mini_3.ogg b/media/vocal/mono/mini_3.ogg deleted file mode 100644 index 18888005d..000000000 Binary files a/media/vocal/mono/mini_3.ogg and /dev/null differ diff --git a/media/vocal/mono/ospin.ogg b/media/vocal/mono/ospin.ogg new file mode 100644 index 000000000..20d0a9d24 Binary files /dev/null and b/media/vocal/mono/ospin.ogg differ diff --git a/media/vocal/mono/ospin0.ogg b/media/vocal/mono/ospin0.ogg new file mode 100644 index 000000000..c55725476 Binary files /dev/null and b/media/vocal/mono/ospin0.ogg differ diff --git a/media/vocal/mono/ospin_1.ogg b/media/vocal/mono/ospin_1.ogg deleted file mode 100644 index 3016ed39a..000000000 Binary files a/media/vocal/mono/ospin_1.ogg and /dev/null differ diff --git a/media/vocal/mono/ospin_2.ogg b/media/vocal/mono/ospin_2.ogg deleted file mode 100644 index 83695304a..000000000 Binary files a/media/vocal/mono/ospin_2.ogg and /dev/null differ diff --git a/media/vocal/mono/ospin_3.ogg b/media/vocal/mono/ospin_3.ogg deleted file mode 100644 index 185f43892..000000000 Binary files a/media/vocal/mono/ospin_3.ogg and /dev/null differ diff --git a/media/vocal/mono/pentacrash_1.ogg b/media/vocal/mono/pentacrash_1.ogg new file mode 100644 index 000000000..948aa8f5d Binary files /dev/null and b/media/vocal/mono/pentacrash_1.ogg differ diff --git a/media/vocal/mono/pentacrash_2.ogg b/media/vocal/mono/pentacrash_2.ogg new file mode 100644 index 000000000..41db171f0 Binary files /dev/null and b/media/vocal/mono/pentacrash_2.ogg differ diff --git a/media/vocal/mono/pentacrash_3.ogg b/media/vocal/mono/pentacrash_3.ogg new file mode 100644 index 000000000..a41017d61 Binary files /dev/null and b/media/vocal/mono/pentacrash_3.ogg differ diff --git a/media/vocal/mono/pentacrash_4.ogg b/media/vocal/mono/pentacrash_4.ogg new file mode 100644 index 000000000..b79348f7e Binary files /dev/null and b/media/vocal/mono/pentacrash_4.ogg differ diff --git a/media/vocal/mono/perfect_clear.ogg b/media/vocal/mono/perfect_clear.ogg deleted file mode 100644 index 9634a49f2..000000000 Binary files a/media/vocal/mono/perfect_clear.ogg and /dev/null differ diff --git a/media/vocal/mono/perfect_clear_1.ogg b/media/vocal/mono/perfect_clear_1.ogg new file mode 100644 index 000000000..7edf99d1b Binary files /dev/null and b/media/vocal/mono/perfect_clear_1.ogg differ diff --git a/media/vocal/mono/perfect_clear_2.ogg b/media/vocal/mono/perfect_clear_2.ogg new file mode 100644 index 000000000..550726c97 Binary files /dev/null and b/media/vocal/mono/perfect_clear_2.ogg differ diff --git a/media/vocal/mono/perfect_clear_3.ogg b/media/vocal/mono/perfect_clear_3.ogg new file mode 100644 index 000000000..7ea10fcfc Binary files /dev/null and b/media/vocal/mono/perfect_clear_3.ogg differ diff --git a/media/vocal/mono/perfect_clear_4.ogg b/media/vocal/mono/perfect_clear_4.ogg new file mode 100644 index 000000000..279d03801 Binary files /dev/null and b/media/vocal/mono/perfect_clear_4.ogg differ diff --git a/media/vocal/mono/single_1.ogg b/media/vocal/mono/single_1.ogg index 6591a2de1..83f3b062b 100644 Binary files a/media/vocal/mono/single_1.ogg and b/media/vocal/mono/single_1.ogg differ diff --git a/media/vocal/mono/single_2.ogg b/media/vocal/mono/single_2.ogg index 3e930847d..1c282225d 100644 Binary files a/media/vocal/mono/single_2.ogg and b/media/vocal/mono/single_2.ogg differ diff --git a/media/vocal/mono/single_3.ogg b/media/vocal/mono/single_3.ogg new file mode 100644 index 000000000..f5fafda9d Binary files /dev/null and b/media/vocal/mono/single_3.ogg differ diff --git a/media/vocal/mono/single_4.ogg b/media/vocal/mono/single_4.ogg new file mode 100644 index 000000000..26a42b968 Binary files /dev/null and b/media/vocal/mono/single_4.ogg differ diff --git a/media/vocal/mono/sspin.ogg b/media/vocal/mono/sspin.ogg new file mode 100644 index 000000000..05569f799 Binary files /dev/null and b/media/vocal/mono/sspin.ogg differ diff --git a/media/vocal/mono/sspin0.ogg b/media/vocal/mono/sspin0.ogg new file mode 100644 index 000000000..c8ba8dc2e Binary files /dev/null and b/media/vocal/mono/sspin0.ogg differ diff --git a/media/vocal/mono/sspin_1.ogg b/media/vocal/mono/sspin_1.ogg deleted file mode 100644 index 97ae12a9d..000000000 Binary files a/media/vocal/mono/sspin_1.ogg and /dev/null differ diff --git a/media/vocal/mono/sspin_2.ogg b/media/vocal/mono/sspin_2.ogg deleted file mode 100644 index 50b1f8bda..000000000 Binary files a/media/vocal/mono/sspin_2.ogg and /dev/null differ diff --git a/media/vocal/mono/sspin_3.ogg b/media/vocal/mono/sspin_3.ogg deleted file mode 100644 index e3c444236..000000000 Binary files a/media/vocal/mono/sspin_3.ogg and /dev/null differ diff --git a/media/vocal/mono/techrash_1.ogg b/media/vocal/mono/techrash_1.ogg index 77d1dbbde..181a350e4 100644 Binary files a/media/vocal/mono/techrash_1.ogg and b/media/vocal/mono/techrash_1.ogg differ diff --git a/media/vocal/mono/techrash_2.ogg b/media/vocal/mono/techrash_2.ogg index 4b9578377..06a44db02 100644 Binary files a/media/vocal/mono/techrash_2.ogg and b/media/vocal/mono/techrash_2.ogg differ diff --git a/media/vocal/mono/techrash_3.ogg b/media/vocal/mono/techrash_3.ogg new file mode 100644 index 000000000..11a699d4b Binary files /dev/null and b/media/vocal/mono/techrash_3.ogg differ diff --git a/media/vocal/mono/techrash_4.ogg b/media/vocal/mono/techrash_4.ogg new file mode 100644 index 000000000..4491bb7d5 Binary files /dev/null and b/media/vocal/mono/techrash_4.ogg differ diff --git a/media/vocal/mono/test_1.ogg b/media/vocal/mono/test_1.ogg index ce261bdb8..78b66cb7e 100644 Binary files a/media/vocal/mono/test_1.ogg and b/media/vocal/mono/test_1.ogg differ diff --git a/media/vocal/mono/test_2.ogg b/media/vocal/mono/test_2.ogg index 4720a201f..3bc1f4f09 100644 Binary files a/media/vocal/mono/test_2.ogg and b/media/vocal/mono/test_2.ogg differ diff --git a/media/vocal/mono/test_3.ogg b/media/vocal/mono/test_3.ogg new file mode 100644 index 000000000..6cbbb6d11 Binary files /dev/null and b/media/vocal/mono/test_3.ogg differ diff --git a/media/vocal/mono/test_4.ogg b/media/vocal/mono/test_4.ogg new file mode 100644 index 000000000..71a95645a Binary files /dev/null and b/media/vocal/mono/test_4.ogg differ diff --git a/media/vocal/mono/triple.ogg b/media/vocal/mono/triple.ogg deleted file mode 100644 index dade3b06e..000000000 Binary files a/media/vocal/mono/triple.ogg and /dev/null differ diff --git a/media/vocal/mono/triple_1.ogg b/media/vocal/mono/triple_1.ogg index 562475dc7..8eb3de1ca 100644 Binary files a/media/vocal/mono/triple_1.ogg and b/media/vocal/mono/triple_1.ogg differ diff --git a/media/vocal/mono/triple_2.ogg b/media/vocal/mono/triple_2.ogg new file mode 100644 index 000000000..11880edf7 Binary files /dev/null and b/media/vocal/mono/triple_2.ogg differ diff --git a/media/vocal/mono/triple_3.ogg b/media/vocal/mono/triple_3.ogg new file mode 100644 index 000000000..216786262 Binary files /dev/null and b/media/vocal/mono/triple_3.ogg differ diff --git a/media/vocal/mono/triple_4.ogg b/media/vocal/mono/triple_4.ogg new file mode 100644 index 000000000..d3af588bb Binary files /dev/null and b/media/vocal/mono/triple_4.ogg differ diff --git a/media/vocal/mono/tspin.ogg b/media/vocal/mono/tspin.ogg new file mode 100644 index 000000000..8be45f917 Binary files /dev/null and b/media/vocal/mono/tspin.ogg differ diff --git a/media/vocal/mono/tspin0.ogg b/media/vocal/mono/tspin0.ogg new file mode 100644 index 000000000..dfc91b687 Binary files /dev/null and b/media/vocal/mono/tspin0.ogg differ diff --git a/media/vocal/mono/tspin_1.ogg b/media/vocal/mono/tspin_1.ogg deleted file mode 100644 index 1d2dc3abd..000000000 Binary files a/media/vocal/mono/tspin_1.ogg and /dev/null differ diff --git a/media/vocal/mono/tspin_2.ogg b/media/vocal/mono/tspin_2.ogg deleted file mode 100644 index a7fecadf8..000000000 Binary files a/media/vocal/mono/tspin_2.ogg and /dev/null differ diff --git a/media/vocal/mono/tspin_3.ogg b/media/vocal/mono/tspin_3.ogg deleted file mode 100644 index f0dad5a33..000000000 Binary files a/media/vocal/mono/tspin_3.ogg and /dev/null differ diff --git a/media/vocal/mono/welcome_1.ogg b/media/vocal/mono/welcome_1.ogg index d8f56e004..34008c076 100644 Binary files a/media/vocal/mono/welcome_1.ogg and b/media/vocal/mono/welcome_1.ogg differ diff --git a/media/vocal/mono/welcome_2.ogg b/media/vocal/mono/welcome_2.ogg index d7ba3b372..04b636e5a 100644 Binary files a/media/vocal/mono/welcome_2.ogg and b/media/vocal/mono/welcome_2.ogg differ diff --git a/media/vocal/mono/welcome_3.ogg b/media/vocal/mono/welcome_3.ogg index db690c620..97e1e500d 100644 Binary files a/media/vocal/mono/welcome_3.ogg and b/media/vocal/mono/welcome_3.ogg differ diff --git a/media/vocal/mono/welcome_4.ogg b/media/vocal/mono/welcome_4.ogg new file mode 100644 index 000000000..b6eeb8b0f Binary files /dev/null and b/media/vocal/mono/welcome_4.ogg differ diff --git a/media/vocal/mono/welcome_5.ogg b/media/vocal/mono/welcome_5.ogg new file mode 100644 index 000000000..c88e9ae9a Binary files /dev/null and b/media/vocal/mono/welcome_5.ogg differ diff --git a/media/vocal/mono/win_1.ogg b/media/vocal/mono/win_1.ogg index 1a733e9f2..dc4f023b0 100644 Binary files a/media/vocal/mono/win_1.ogg and b/media/vocal/mono/win_1.ogg differ diff --git a/media/vocal/mono/win_2.ogg b/media/vocal/mono/win_2.ogg index 94e80c8bf..bfab69057 100644 Binary files a/media/vocal/mono/win_2.ogg and b/media/vocal/mono/win_2.ogg differ diff --git a/media/vocal/mono/win_3.ogg b/media/vocal/mono/win_3.ogg index 04e8a1335..c159100a8 100644 Binary files a/media/vocal/mono/win_3.ogg and b/media/vocal/mono/win_3.ogg differ diff --git a/media/vocal/mono/win_4.ogg b/media/vocal/mono/win_4.ogg new file mode 100644 index 000000000..b49f3e755 Binary files /dev/null and b/media/vocal/mono/win_4.ogg differ diff --git a/media/vocal/mono/win_5.ogg b/media/vocal/mono/win_5.ogg new file mode 100644 index 000000000..dbfcb85ae Binary files /dev/null and b/media/vocal/mono/win_5.ogg differ diff --git a/media/vocal/mono/zspin.ogg b/media/vocal/mono/zspin.ogg new file mode 100644 index 000000000..358de5c9e Binary files /dev/null and b/media/vocal/mono/zspin.ogg differ diff --git a/media/vocal/mono/zspin0.ogg b/media/vocal/mono/zspin0.ogg new file mode 100644 index 000000000..46c2b96d7 Binary files /dev/null and b/media/vocal/mono/zspin0.ogg differ diff --git a/media/vocal/mono/zspin_1.ogg b/media/vocal/mono/zspin_1.ogg deleted file mode 100644 index 65508f39b..000000000 Binary files a/media/vocal/mono/zspin_1.ogg and /dev/null differ diff --git a/media/vocal/mono/zspin_2.ogg b/media/vocal/mono/zspin_2.ogg deleted file mode 100644 index a7b77163d..000000000 Binary files a/media/vocal/mono/zspin_2.ogg and /dev/null differ diff --git a/media/vocal/mono/zspin_3.ogg b/media/vocal/mono/zspin_3.ogg deleted file mode 100644 index 91c6157bb..000000000 Binary files a/media/vocal/mono/zspin_3.ogg and /dev/null differ diff --git a/media/vocal/neuro/air.ogg b/media/vocal/neuro/air.ogg new file mode 100644 index 000000000..c44dab338 Binary files /dev/null and b/media/vocal/neuro/air.ogg differ diff --git a/media/vocal/neuro/all_clear.ogg b/media/vocal/neuro/all_clear.ogg new file mode 100644 index 000000000..2e8479683 Binary files /dev/null and b/media/vocal/neuro/all_clear.ogg differ diff --git a/media/vocal/neuro/b2b.ogg b/media/vocal/neuro/b2b.ogg new file mode 100644 index 000000000..c45f4edf1 Binary files /dev/null and b/media/vocal/neuro/b2b.ogg differ diff --git a/media/vocal/neuro/b3b.ogg b/media/vocal/neuro/b3b.ogg new file mode 100644 index 000000000..0cdc03618 Binary files /dev/null and b/media/vocal/neuro/b3b.ogg differ diff --git a/media/vocal/neuro/bye.ogg b/media/vocal/neuro/bye.ogg new file mode 100644 index 000000000..feec30a05 Binary files /dev/null and b/media/vocal/neuro/bye.ogg differ diff --git a/media/vocal/neuro/clear.ogg b/media/vocal/neuro/clear.ogg new file mode 100644 index 000000000..59c80d4c4 Binary files /dev/null and b/media/vocal/neuro/clear.ogg differ diff --git a/media/vocal/neuro/colour.ogg b/media/vocal/neuro/colour.ogg new file mode 100644 index 000000000..6b9cd6e50 Binary files /dev/null and b/media/vocal/neuro/colour.ogg differ diff --git a/media/vocal/neuro/cspin.ogg b/media/vocal/neuro/cspin.ogg new file mode 100644 index 000000000..4fff018c7 Binary files /dev/null and b/media/vocal/neuro/cspin.ogg differ diff --git a/media/vocal/neuro/cspin0.ogg b/media/vocal/neuro/cspin0.ogg new file mode 100644 index 000000000..77a2f0b48 Binary files /dev/null and b/media/vocal/neuro/cspin0.ogg differ diff --git a/media/vocal/neuro/decacrash.ogg b/media/vocal/neuro/decacrash.ogg new file mode 100644 index 000000000..88068396c Binary files /dev/null and b/media/vocal/neuro/decacrash.ogg differ diff --git a/media/vocal/neuro/deep.ogg b/media/vocal/neuro/deep.ogg new file mode 100644 index 000000000..c9b7b49a2 Binary files /dev/null and b/media/vocal/neuro/deep.ogg differ diff --git a/media/vocal/neuro/dodecacrash.ogg b/media/vocal/neuro/dodecacrash.ogg new file mode 100644 index 000000000..4197eeee1 Binary files /dev/null and b/media/vocal/neuro/dodecacrash.ogg differ diff --git a/media/vocal/neuro/double.ogg b/media/vocal/neuro/double.ogg new file mode 100644 index 000000000..e66adf8bb Binary files /dev/null and b/media/vocal/neuro/double.ogg differ diff --git a/media/vocal/neuro/doubt.ogg b/media/vocal/neuro/doubt.ogg new file mode 100644 index 000000000..fc0be54d3 Binary files /dev/null and b/media/vocal/neuro/doubt.ogg differ diff --git a/media/vocal/neuro/espin.ogg b/media/vocal/neuro/espin.ogg new file mode 100644 index 000000000..ac46bb32d Binary files /dev/null and b/media/vocal/neuro/espin.ogg differ diff --git a/media/vocal/neuro/espin0.ogg b/media/vocal/neuro/espin0.ogg new file mode 100644 index 000000000..78160caa1 Binary files /dev/null and b/media/vocal/neuro/espin0.ogg differ diff --git a/media/vocal/neuro/fspin.ogg b/media/vocal/neuro/fspin.ogg new file mode 100644 index 000000000..f06304944 Binary files /dev/null and b/media/vocal/neuro/fspin.ogg differ diff --git a/media/vocal/neuro/fspin0.ogg b/media/vocal/neuro/fspin0.ogg new file mode 100644 index 000000000..825ed8d8a Binary files /dev/null and b/media/vocal/neuro/fspin0.ogg differ diff --git a/media/vocal/neuro/half_clear.ogg b/media/vocal/neuro/half_clear.ogg new file mode 100644 index 000000000..13036dc4a Binary files /dev/null and b/media/vocal/neuro/half_clear.ogg differ diff --git a/media/vocal/neuro/happy.ogg b/media/vocal/neuro/happy.ogg new file mode 100644 index 000000000..cc2cf43ec Binary files /dev/null and b/media/vocal/neuro/happy.ogg differ diff --git a/media/vocal/neuro/heptacrash.ogg b/media/vocal/neuro/heptacrash.ogg new file mode 100644 index 000000000..f2f6396b0 Binary files /dev/null and b/media/vocal/neuro/heptacrash.ogg differ diff --git a/media/vocal/neuro/heptadecacrash.ogg b/media/vocal/neuro/heptadecacrash.ogg new file mode 100644 index 000000000..bebc71f2d Binary files /dev/null and b/media/vocal/neuro/heptadecacrash.ogg differ diff --git a/media/vocal/neuro/hexacrash.ogg b/media/vocal/neuro/hexacrash.ogg new file mode 100644 index 000000000..ec07c1fea Binary files /dev/null and b/media/vocal/neuro/hexacrash.ogg differ diff --git a/media/vocal/neuro/hexadecacrash.ogg b/media/vocal/neuro/hexadecacrash.ogg new file mode 100644 index 000000000..5d06ead5f Binary files /dev/null and b/media/vocal/neuro/hexadecacrash.ogg differ diff --git a/media/vocal/neuro/hspin.ogg b/media/vocal/neuro/hspin.ogg new file mode 100644 index 000000000..ff5049159 Binary files /dev/null and b/media/vocal/neuro/hspin.ogg differ diff --git a/media/vocal/neuro/hspin0.ogg b/media/vocal/neuro/hspin0.ogg new file mode 100644 index 000000000..dd9cf9ec5 Binary files /dev/null and b/media/vocal/neuro/hspin0.ogg differ diff --git a/media/vocal/neuro/impossicrash.ogg b/media/vocal/neuro/impossicrash.ogg new file mode 100644 index 000000000..0803a25a8 Binary files /dev/null and b/media/vocal/neuro/impossicrash.ogg differ diff --git a/media/vocal/neuro/ispin.ogg b/media/vocal/neuro/ispin.ogg new file mode 100644 index 000000000..5c6a818cd Binary files /dev/null and b/media/vocal/neuro/ispin.ogg differ diff --git a/media/vocal/neuro/ispin0.ogg b/media/vocal/neuro/ispin0.ogg new file mode 100644 index 000000000..c57ceb6bd Binary files /dev/null and b/media/vocal/neuro/ispin0.ogg differ diff --git a/media/vocal/neuro/jspin.ogg b/media/vocal/neuro/jspin.ogg new file mode 100644 index 000000000..a8e41144a Binary files /dev/null and b/media/vocal/neuro/jspin.ogg differ diff --git a/media/vocal/neuro/jspin0.ogg b/media/vocal/neuro/jspin0.ogg new file mode 100644 index 000000000..9ac8384a4 Binary files /dev/null and b/media/vocal/neuro/jspin0.ogg differ diff --git a/media/vocal/neuro/lose.ogg b/media/vocal/neuro/lose.ogg new file mode 100644 index 000000000..253f291cb Binary files /dev/null and b/media/vocal/neuro/lose.ogg differ diff --git a/media/vocal/neuro/lose0.ogg b/media/vocal/neuro/lose0.ogg new file mode 100644 index 000000000..5cfef8af2 Binary files /dev/null and b/media/vocal/neuro/lose0.ogg differ diff --git a/media/vocal/neuro/lspin.ogg b/media/vocal/neuro/lspin.ogg new file mode 100644 index 000000000..0d220b4b9 Binary files /dev/null and b/media/vocal/neuro/lspin.ogg differ diff --git a/media/vocal/neuro/lspin0.ogg b/media/vocal/neuro/lspin0.ogg new file mode 100644 index 000000000..1de83e296 Binary files /dev/null and b/media/vocal/neuro/lspin0.ogg differ diff --git a/media/vocal/neuro/mini.ogg b/media/vocal/neuro/mini.ogg new file mode 100644 index 000000000..859417c75 Binary files /dev/null and b/media/vocal/neuro/mini.ogg differ diff --git a/media/vocal/neuro/mix.ogg b/media/vocal/neuro/mix.ogg new file mode 100644 index 000000000..4ccf37fb7 Binary files /dev/null and b/media/vocal/neuro/mix.ogg differ diff --git a/media/vocal/neuro/nonacrash.ogg b/media/vocal/neuro/nonacrash.ogg new file mode 100644 index 000000000..69c65c421 Binary files /dev/null and b/media/vocal/neuro/nonacrash.ogg differ diff --git a/media/vocal/neuro/nonadecacrash.ogg b/media/vocal/neuro/nonadecacrash.ogg new file mode 100644 index 000000000..c4d4434d0 Binary files /dev/null and b/media/vocal/neuro/nonadecacrash.ogg differ diff --git a/media/vocal/neuro/nspin.ogg b/media/vocal/neuro/nspin.ogg new file mode 100644 index 000000000..765502f3c Binary files /dev/null and b/media/vocal/neuro/nspin.ogg differ diff --git a/media/vocal/neuro/nspin0.ogg b/media/vocal/neuro/nspin0.ogg new file mode 100644 index 000000000..70cf9ba64 Binary files /dev/null and b/media/vocal/neuro/nspin0.ogg differ diff --git a/media/vocal/neuro/octacrash.ogg b/media/vocal/neuro/octacrash.ogg new file mode 100644 index 000000000..58967789f Binary files /dev/null and b/media/vocal/neuro/octacrash.ogg differ diff --git a/media/vocal/neuro/octadecacrash.ogg b/media/vocal/neuro/octadecacrash.ogg new file mode 100644 index 000000000..8c1963b13 Binary files /dev/null and b/media/vocal/neuro/octadecacrash.ogg differ diff --git a/media/vocal/neuro/ospin.ogg b/media/vocal/neuro/ospin.ogg new file mode 100644 index 000000000..6c0b4e270 Binary files /dev/null and b/media/vocal/neuro/ospin.ogg differ diff --git a/media/vocal/neuro/ospin0.ogg b/media/vocal/neuro/ospin0.ogg new file mode 100644 index 000000000..0a64729a6 Binary files /dev/null and b/media/vocal/neuro/ospin0.ogg differ diff --git a/media/vocal/neuro/pentacrash.ogg b/media/vocal/neuro/pentacrash.ogg new file mode 100644 index 000000000..2e852c238 Binary files /dev/null and b/media/vocal/neuro/pentacrash.ogg differ diff --git a/media/vocal/neuro/pentadecacrash.ogg b/media/vocal/neuro/pentadecacrash.ogg new file mode 100644 index 000000000..d768c99bc Binary files /dev/null and b/media/vocal/neuro/pentadecacrash.ogg differ diff --git a/media/vocal/neuro/perfect_clear.ogg b/media/vocal/neuro/perfect_clear.ogg new file mode 100644 index 000000000..b1d51ab57 Binary files /dev/null and b/media/vocal/neuro/perfect_clear.ogg differ diff --git a/media/vocal/neuro/pspin.ogg b/media/vocal/neuro/pspin.ogg new file mode 100644 index 000000000..f4bbe920a Binary files /dev/null and b/media/vocal/neuro/pspin.ogg differ diff --git a/media/vocal/neuro/pspin0.ogg b/media/vocal/neuro/pspin0.ogg new file mode 100644 index 000000000..0c5bf2ff5 Binary files /dev/null and b/media/vocal/neuro/pspin0.ogg differ diff --git a/media/vocal/neuro/qspin.ogg b/media/vocal/neuro/qspin.ogg new file mode 100644 index 000000000..9a41e96a7 Binary files /dev/null and b/media/vocal/neuro/qspin.ogg differ diff --git a/media/vocal/neuro/qspin0.ogg b/media/vocal/neuro/qspin0.ogg new file mode 100644 index 000000000..8ab2f009d Binary files /dev/null and b/media/vocal/neuro/qspin0.ogg differ diff --git a/media/vocal/neuro/rspin.ogg b/media/vocal/neuro/rspin.ogg new file mode 100644 index 000000000..256cf25ce Binary files /dev/null and b/media/vocal/neuro/rspin.ogg differ diff --git a/media/vocal/neuro/rspin0.ogg b/media/vocal/neuro/rspin0.ogg new file mode 100644 index 000000000..a25ad4fe8 Binary files /dev/null and b/media/vocal/neuro/rspin0.ogg differ diff --git a/media/vocal/neuro/single.ogg b/media/vocal/neuro/single.ogg new file mode 100644 index 000000000..6eb34f7f5 Binary files /dev/null and b/media/vocal/neuro/single.ogg differ diff --git a/media/vocal/neuro/split.ogg b/media/vocal/neuro/split.ogg new file mode 100644 index 000000000..b7c288792 Binary files /dev/null and b/media/vocal/neuro/split.ogg differ diff --git a/media/vocal/neuro/sspin.ogg b/media/vocal/neuro/sspin.ogg new file mode 100644 index 000000000..ff92c7f3b Binary files /dev/null and b/media/vocal/neuro/sspin.ogg differ diff --git a/media/vocal/neuro/sspin0.ogg b/media/vocal/neuro/sspin0.ogg new file mode 100644 index 000000000..3782f6630 Binary files /dev/null and b/media/vocal/neuro/sspin0.ogg differ diff --git a/media/vocal/neuro/techrash.ogg b/media/vocal/neuro/techrash.ogg new file mode 100644 index 000000000..354baeb81 Binary files /dev/null and b/media/vocal/neuro/techrash.ogg differ diff --git a/media/vocal/neuro/test.ogg b/media/vocal/neuro/test.ogg new file mode 100644 index 000000000..85fbd979e Binary files /dev/null and b/media/vocal/neuro/test.ogg differ diff --git a/media/vocal/neuro/tetradecacrash.ogg b/media/vocal/neuro/tetradecacrash.ogg new file mode 100644 index 000000000..cd26e71fa Binary files /dev/null and b/media/vocal/neuro/tetradecacrash.ogg differ diff --git a/media/vocal/neuro/tridecacrash.ogg b/media/vocal/neuro/tridecacrash.ogg new file mode 100644 index 000000000..03e44ae6c Binary files /dev/null and b/media/vocal/neuro/tridecacrash.ogg differ diff --git a/media/vocal/neuro/triple.ogg b/media/vocal/neuro/triple.ogg new file mode 100644 index 000000000..f334b6a70 Binary files /dev/null and b/media/vocal/neuro/triple.ogg differ diff --git a/media/vocal/neuro/tspin.ogg b/media/vocal/neuro/tspin.ogg new file mode 100644 index 000000000..40a8da7a0 Binary files /dev/null and b/media/vocal/neuro/tspin.ogg differ diff --git a/media/vocal/neuro/tspin0.ogg b/media/vocal/neuro/tspin0.ogg new file mode 100644 index 000000000..6174cf34e Binary files /dev/null and b/media/vocal/neuro/tspin0.ogg differ diff --git a/media/vocal/neuro/ultracrash.ogg b/media/vocal/neuro/ultracrash.ogg new file mode 100644 index 000000000..900bcf453 Binary files /dev/null and b/media/vocal/neuro/ultracrash.ogg differ diff --git a/media/vocal/neuro/undecacrash.ogg b/media/vocal/neuro/undecacrash.ogg new file mode 100644 index 000000000..2548292cc Binary files /dev/null and b/media/vocal/neuro/undecacrash.ogg differ diff --git a/media/vocal/neuro/uspin.ogg b/media/vocal/neuro/uspin.ogg new file mode 100644 index 000000000..d0804df25 Binary files /dev/null and b/media/vocal/neuro/uspin.ogg differ diff --git a/media/vocal/neuro/uspin0.ogg b/media/vocal/neuro/uspin0.ogg new file mode 100644 index 000000000..9e5c2624d Binary files /dev/null and b/media/vocal/neuro/uspin0.ogg differ diff --git a/media/vocal/neuro/vspin.ogg b/media/vocal/neuro/vspin.ogg new file mode 100644 index 000000000..c5e086946 Binary files /dev/null and b/media/vocal/neuro/vspin.ogg differ diff --git a/media/vocal/neuro/vspin0.ogg b/media/vocal/neuro/vspin0.ogg new file mode 100644 index 000000000..bdacd4dc7 Binary files /dev/null and b/media/vocal/neuro/vspin0.ogg differ diff --git a/media/vocal/neuro/welcome.ogg b/media/vocal/neuro/welcome.ogg new file mode 100644 index 000000000..503fdbe70 Binary files /dev/null and b/media/vocal/neuro/welcome.ogg differ diff --git a/media/vocal/neuro/win.ogg b/media/vocal/neuro/win.ogg new file mode 100644 index 000000000..17db5ecf8 Binary files /dev/null and b/media/vocal/neuro/win.ogg differ diff --git a/media/vocal/neuro/win0.ogg b/media/vocal/neuro/win0.ogg new file mode 100644 index 000000000..95b134fb9 Binary files /dev/null and b/media/vocal/neuro/win0.ogg differ diff --git a/media/vocal/neuro/wspin.ogg b/media/vocal/neuro/wspin.ogg new file mode 100644 index 000000000..74ade0622 Binary files /dev/null and b/media/vocal/neuro/wspin.ogg differ diff --git a/media/vocal/neuro/wspin0.ogg b/media/vocal/neuro/wspin0.ogg new file mode 100644 index 000000000..53485955d Binary files /dev/null and b/media/vocal/neuro/wspin0.ogg differ diff --git a/media/vocal/neuro/xspin.ogg b/media/vocal/neuro/xspin.ogg new file mode 100644 index 000000000..c9543fe48 Binary files /dev/null and b/media/vocal/neuro/xspin.ogg differ diff --git a/media/vocal/neuro/xspin0.ogg b/media/vocal/neuro/xspin0.ogg new file mode 100644 index 000000000..6f6a8da83 Binary files /dev/null and b/media/vocal/neuro/xspin0.ogg differ diff --git a/media/vocal/neuro/yspin.ogg b/media/vocal/neuro/yspin.ogg new file mode 100644 index 000000000..6c63a1c46 Binary files /dev/null and b/media/vocal/neuro/yspin.ogg differ diff --git a/media/vocal/neuro/yspin0.ogg b/media/vocal/neuro/yspin0.ogg new file mode 100644 index 000000000..92fbfadde Binary files /dev/null and b/media/vocal/neuro/yspin0.ogg differ diff --git a/media/vocal/neuro/zspin.ogg b/media/vocal/neuro/zspin.ogg new file mode 100644 index 000000000..3902c232e Binary files /dev/null and b/media/vocal/neuro/zspin.ogg differ diff --git a/media/vocal/neuro/zspin0.ogg b/media/vocal/neuro/zspin0.ogg new file mode 100644 index 000000000..d80a5c756 Binary files /dev/null and b/media/vocal/neuro/zspin0.ogg differ diff --git a/media/vocal/zundamon/air.ogg b/media/vocal/zundamon/air.ogg new file mode 100644 index 000000000..b18c02649 Binary files /dev/null and b/media/vocal/zundamon/air.ogg differ diff --git a/media/vocal/zundamon/b2b.ogg b/media/vocal/zundamon/b2b.ogg new file mode 100644 index 000000000..608b25b2d Binary files /dev/null and b/media/vocal/zundamon/b2b.ogg differ diff --git a/media/vocal/zundamon/b3b.ogg b/media/vocal/zundamon/b3b.ogg new file mode 100644 index 000000000..1b4f3c761 Binary files /dev/null and b/media/vocal/zundamon/b3b.ogg differ diff --git a/media/vocal/zundamon/bye.ogg b/media/vocal/zundamon/bye.ogg new file mode 100644 index 000000000..51b03349c Binary files /dev/null and b/media/vocal/zundamon/bye.ogg differ diff --git a/media/vocal/zundamon/color.ogg b/media/vocal/zundamon/color.ogg new file mode 100644 index 000000000..983ff29f8 Binary files /dev/null and b/media/vocal/zundamon/color.ogg differ diff --git a/media/vocal/zundamon/cspin.ogg b/media/vocal/zundamon/cspin.ogg new file mode 100644 index 000000000..6e2dc58fa Binary files /dev/null and b/media/vocal/zundamon/cspin.ogg differ diff --git a/media/vocal/zundamon/cspin0.ogg b/media/vocal/zundamon/cspin0.ogg new file mode 100644 index 000000000..e5c99caee Binary files /dev/null and b/media/vocal/zundamon/cspin0.ogg differ diff --git a/media/vocal/zundamon/decacrash.ogg b/media/vocal/zundamon/decacrash.ogg new file mode 100644 index 000000000..902c1a7ef Binary files /dev/null and b/media/vocal/zundamon/decacrash.ogg differ diff --git a/media/vocal/zundamon/deep.ogg b/media/vocal/zundamon/deep.ogg new file mode 100644 index 000000000..e447e63bc Binary files /dev/null and b/media/vocal/zundamon/deep.ogg differ diff --git a/media/vocal/zundamon/dodecacrash.ogg b/media/vocal/zundamon/dodecacrash.ogg new file mode 100644 index 000000000..96664eede Binary files /dev/null and b/media/vocal/zundamon/dodecacrash.ogg differ diff --git a/media/vocal/zundamon/double.ogg b/media/vocal/zundamon/double.ogg new file mode 100644 index 000000000..3cd5e9538 Binary files /dev/null and b/media/vocal/zundamon/double.ogg differ diff --git a/media/vocal/zundamon/doubt.ogg b/media/vocal/zundamon/doubt.ogg new file mode 100644 index 000000000..ce6664ed8 Binary files /dev/null and b/media/vocal/zundamon/doubt.ogg differ diff --git a/media/vocal/zundamon/espin.ogg b/media/vocal/zundamon/espin.ogg new file mode 100644 index 000000000..805d352ad Binary files /dev/null and b/media/vocal/zundamon/espin.ogg differ diff --git a/media/vocal/zundamon/espin0.ogg b/media/vocal/zundamon/espin0.ogg new file mode 100644 index 000000000..14cceddc8 Binary files /dev/null and b/media/vocal/zundamon/espin0.ogg differ diff --git a/media/vocal/zundamon/fspin.ogg b/media/vocal/zundamon/fspin.ogg new file mode 100644 index 000000000..9cc49d3f4 Binary files /dev/null and b/media/vocal/zundamon/fspin.ogg differ diff --git a/media/vocal/zundamon/fspin0.ogg b/media/vocal/zundamon/fspin0.ogg new file mode 100644 index 000000000..891c4bca6 Binary files /dev/null and b/media/vocal/zundamon/fspin0.ogg differ diff --git a/media/vocal/zundamon/half_clear_1.ogg b/media/vocal/zundamon/half_clear_1.ogg new file mode 100644 index 000000000..39b15e2d2 Binary files /dev/null and b/media/vocal/zundamon/half_clear_1.ogg differ diff --git a/media/vocal/zundamon/half_clear_2.ogg b/media/vocal/zundamon/half_clear_2.ogg new file mode 100644 index 000000000..cd0a1932d Binary files /dev/null and b/media/vocal/zundamon/half_clear_2.ogg differ diff --git a/media/vocal/zundamon/happy.ogg b/media/vocal/zundamon/happy.ogg new file mode 100644 index 000000000..5c472c13c Binary files /dev/null and b/media/vocal/zundamon/happy.ogg differ diff --git a/media/vocal/zundamon/heptacrash.ogg b/media/vocal/zundamon/heptacrash.ogg new file mode 100644 index 000000000..75a059927 Binary files /dev/null and b/media/vocal/zundamon/heptacrash.ogg differ diff --git a/media/vocal/zundamon/heptadecacrash.ogg b/media/vocal/zundamon/heptadecacrash.ogg new file mode 100644 index 000000000..3a69ac96a Binary files /dev/null and b/media/vocal/zundamon/heptadecacrash.ogg differ diff --git a/media/vocal/zundamon/hexacrash.ogg b/media/vocal/zundamon/hexacrash.ogg new file mode 100644 index 000000000..09d6bae8f Binary files /dev/null and b/media/vocal/zundamon/hexacrash.ogg differ diff --git a/media/vocal/zundamon/hexadecacrash.ogg b/media/vocal/zundamon/hexadecacrash.ogg new file mode 100644 index 000000000..fcb30b0c5 Binary files /dev/null and b/media/vocal/zundamon/hexadecacrash.ogg differ diff --git a/media/vocal/zundamon/hspin.ogg b/media/vocal/zundamon/hspin.ogg new file mode 100644 index 000000000..2e8ba80e1 Binary files /dev/null and b/media/vocal/zundamon/hspin.ogg differ diff --git a/media/vocal/zundamon/hspin0.ogg b/media/vocal/zundamon/hspin0.ogg new file mode 100644 index 000000000..2bfbd5ba3 Binary files /dev/null and b/media/vocal/zundamon/hspin0.ogg differ diff --git a/media/vocal/zundamon/impossicrash.ogg b/media/vocal/zundamon/impossicrash.ogg new file mode 100644 index 000000000..6ad866ba1 Binary files /dev/null and b/media/vocal/zundamon/impossicrash.ogg differ diff --git a/media/vocal/zundamon/ispin.ogg b/media/vocal/zundamon/ispin.ogg new file mode 100644 index 000000000..41bba14e7 Binary files /dev/null and b/media/vocal/zundamon/ispin.ogg differ diff --git a/media/vocal/zundamon/ispin0.ogg b/media/vocal/zundamon/ispin0.ogg new file mode 100644 index 000000000..2d1c226d2 Binary files /dev/null and b/media/vocal/zundamon/ispin0.ogg differ diff --git a/media/vocal/zundamon/jspin.ogg b/media/vocal/zundamon/jspin.ogg new file mode 100644 index 000000000..02fad4414 Binary files /dev/null and b/media/vocal/zundamon/jspin.ogg differ diff --git a/media/vocal/zundamon/jspin0.ogg b/media/vocal/zundamon/jspin0.ogg new file mode 100644 index 000000000..31c238211 Binary files /dev/null and b/media/vocal/zundamon/jspin0.ogg differ diff --git a/media/vocal/zundamon/lose_1.ogg b/media/vocal/zundamon/lose_1.ogg new file mode 100644 index 000000000..e1a30e9e1 Binary files /dev/null and b/media/vocal/zundamon/lose_1.ogg differ diff --git a/media/vocal/zundamon/lose_2.ogg b/media/vocal/zundamon/lose_2.ogg new file mode 100644 index 000000000..a0b37f328 Binary files /dev/null and b/media/vocal/zundamon/lose_2.ogg differ diff --git a/media/vocal/zundamon/lspin.ogg b/media/vocal/zundamon/lspin.ogg new file mode 100644 index 000000000..3d25fe108 Binary files /dev/null and b/media/vocal/zundamon/lspin.ogg differ diff --git a/media/vocal/zundamon/lspin0.ogg b/media/vocal/zundamon/lspin0.ogg new file mode 100644 index 000000000..3c5caa617 Binary files /dev/null and b/media/vocal/zundamon/lspin0.ogg differ diff --git a/media/vocal/zundamon/mini.ogg b/media/vocal/zundamon/mini.ogg new file mode 100644 index 000000000..5218c0b71 Binary files /dev/null and b/media/vocal/zundamon/mini.ogg differ diff --git a/media/vocal/zundamon/mis.ogg b/media/vocal/zundamon/mis.ogg new file mode 100644 index 000000000..df2b82915 Binary files /dev/null and b/media/vocal/zundamon/mis.ogg differ diff --git a/media/vocal/zundamon/nonacrash.ogg b/media/vocal/zundamon/nonacrash.ogg new file mode 100644 index 000000000..90decaf84 Binary files /dev/null and b/media/vocal/zundamon/nonacrash.ogg differ diff --git a/media/vocal/zundamon/nonadecacrash.ogg b/media/vocal/zundamon/nonadecacrash.ogg new file mode 100644 index 000000000..a46c8aa69 Binary files /dev/null and b/media/vocal/zundamon/nonadecacrash.ogg differ diff --git a/media/vocal/zundamon/nspin.ogg b/media/vocal/zundamon/nspin.ogg new file mode 100644 index 000000000..8b6dc63e1 Binary files /dev/null and b/media/vocal/zundamon/nspin.ogg differ diff --git a/media/vocal/zundamon/nspin0.ogg b/media/vocal/zundamon/nspin0.ogg new file mode 100644 index 000000000..2f84118c3 Binary files /dev/null and b/media/vocal/zundamon/nspin0.ogg differ diff --git a/media/vocal/zundamon/octacrash.ogg b/media/vocal/zundamon/octacrash.ogg new file mode 100644 index 000000000..1b458d28f Binary files /dev/null and b/media/vocal/zundamon/octacrash.ogg differ diff --git a/media/vocal/zundamon/octadecacrash.ogg b/media/vocal/zundamon/octadecacrash.ogg new file mode 100644 index 000000000..31aa8d461 Binary files /dev/null and b/media/vocal/zundamon/octadecacrash.ogg differ diff --git a/media/vocal/zundamon/ospin.ogg b/media/vocal/zundamon/ospin.ogg new file mode 100644 index 000000000..21354bf81 Binary files /dev/null and b/media/vocal/zundamon/ospin.ogg differ diff --git a/media/vocal/zundamon/ospin0.ogg b/media/vocal/zundamon/ospin0.ogg new file mode 100644 index 000000000..8166b62fb Binary files /dev/null and b/media/vocal/zundamon/ospin0.ogg differ diff --git a/media/vocal/zundamon/pentacrash.ogg b/media/vocal/zundamon/pentacrash.ogg new file mode 100644 index 000000000..7aadf737e Binary files /dev/null and b/media/vocal/zundamon/pentacrash.ogg differ diff --git a/media/vocal/zundamon/pentadecacrash.ogg b/media/vocal/zundamon/pentadecacrash.ogg new file mode 100644 index 000000000..3c1f0b791 Binary files /dev/null and b/media/vocal/zundamon/pentadecacrash.ogg differ diff --git a/media/vocal/zundamon/perfect_clear_1.ogg b/media/vocal/zundamon/perfect_clear_1.ogg new file mode 100644 index 000000000..e5bfd37dc Binary files /dev/null and b/media/vocal/zundamon/perfect_clear_1.ogg differ diff --git a/media/vocal/zundamon/perfect_clear_2.ogg b/media/vocal/zundamon/perfect_clear_2.ogg new file mode 100644 index 000000000..f2ba74878 Binary files /dev/null and b/media/vocal/zundamon/perfect_clear_2.ogg differ diff --git a/media/vocal/zundamon/pspin.ogg b/media/vocal/zundamon/pspin.ogg new file mode 100644 index 000000000..8e376b55c Binary files /dev/null and b/media/vocal/zundamon/pspin.ogg differ diff --git a/media/vocal/zundamon/pspin0.ogg b/media/vocal/zundamon/pspin0.ogg new file mode 100644 index 000000000..d7d604f1d Binary files /dev/null and b/media/vocal/zundamon/pspin0.ogg differ diff --git a/media/vocal/zundamon/qspin.ogg b/media/vocal/zundamon/qspin.ogg new file mode 100644 index 000000000..ee668e195 Binary files /dev/null and b/media/vocal/zundamon/qspin.ogg differ diff --git a/media/vocal/zundamon/qspin0.ogg b/media/vocal/zundamon/qspin0.ogg new file mode 100644 index 000000000..092a2bb0d Binary files /dev/null and b/media/vocal/zundamon/qspin0.ogg differ diff --git a/media/vocal/zundamon/rspin.ogg b/media/vocal/zundamon/rspin.ogg new file mode 100644 index 000000000..59692ec68 Binary files /dev/null and b/media/vocal/zundamon/rspin.ogg differ diff --git a/media/vocal/zundamon/rspin0.ogg b/media/vocal/zundamon/rspin0.ogg new file mode 100644 index 000000000..3ead7c168 Binary files /dev/null and b/media/vocal/zundamon/rspin0.ogg differ diff --git a/media/vocal/zundamon/single.ogg b/media/vocal/zundamon/single.ogg new file mode 100644 index 000000000..ca8823617 Binary files /dev/null and b/media/vocal/zundamon/single.ogg differ diff --git a/media/vocal/zundamon/split.ogg b/media/vocal/zundamon/split.ogg new file mode 100644 index 000000000..03a9aa954 Binary files /dev/null and b/media/vocal/zundamon/split.ogg differ diff --git a/media/vocal/zundamon/sspin.ogg b/media/vocal/zundamon/sspin.ogg new file mode 100644 index 000000000..020a63471 Binary files /dev/null and b/media/vocal/zundamon/sspin.ogg differ diff --git a/media/vocal/zundamon/sspin0.ogg b/media/vocal/zundamon/sspin0.ogg new file mode 100644 index 000000000..04507c6c4 Binary files /dev/null and b/media/vocal/zundamon/sspin0.ogg differ diff --git a/media/vocal/zundamon/techrash.ogg b/media/vocal/zundamon/techrash.ogg new file mode 100644 index 000000000..7610bded0 Binary files /dev/null and b/media/vocal/zundamon/techrash.ogg differ diff --git a/media/vocal/zundamon/test.ogg b/media/vocal/zundamon/test.ogg new file mode 100644 index 000000000..f1a028460 Binary files /dev/null and b/media/vocal/zundamon/test.ogg differ diff --git a/media/vocal/zundamon/tetradecacrash.ogg b/media/vocal/zundamon/tetradecacrash.ogg new file mode 100644 index 000000000..20fe2a690 Binary files /dev/null and b/media/vocal/zundamon/tetradecacrash.ogg differ diff --git a/media/vocal/zundamon/tridecacrash.ogg b/media/vocal/zundamon/tridecacrash.ogg new file mode 100644 index 000000000..779e35ec4 Binary files /dev/null and b/media/vocal/zundamon/tridecacrash.ogg differ diff --git a/media/vocal/zundamon/triple.ogg b/media/vocal/zundamon/triple.ogg new file mode 100644 index 000000000..97e0c0c8a Binary files /dev/null and b/media/vocal/zundamon/triple.ogg differ diff --git a/media/vocal/zundamon/tspin.ogg b/media/vocal/zundamon/tspin.ogg new file mode 100644 index 000000000..c2fed4e45 Binary files /dev/null and b/media/vocal/zundamon/tspin.ogg differ diff --git a/media/vocal/zundamon/tspin0.ogg b/media/vocal/zundamon/tspin0.ogg new file mode 100644 index 000000000..7ffede935 Binary files /dev/null and b/media/vocal/zundamon/tspin0.ogg differ diff --git a/media/vocal/zundamon/ultracrash.ogg b/media/vocal/zundamon/ultracrash.ogg new file mode 100644 index 000000000..a4aaf7e58 Binary files /dev/null and b/media/vocal/zundamon/ultracrash.ogg differ diff --git a/media/vocal/zundamon/undecacrash.ogg b/media/vocal/zundamon/undecacrash.ogg new file mode 100644 index 000000000..d2bf95040 Binary files /dev/null and b/media/vocal/zundamon/undecacrash.ogg differ diff --git a/media/vocal/zundamon/uspin.ogg b/media/vocal/zundamon/uspin.ogg new file mode 100644 index 000000000..4e06a7c62 Binary files /dev/null and b/media/vocal/zundamon/uspin.ogg differ diff --git a/media/vocal/zundamon/uspin0.ogg b/media/vocal/zundamon/uspin0.ogg new file mode 100644 index 000000000..d9795ba87 Binary files /dev/null and b/media/vocal/zundamon/uspin0.ogg differ diff --git a/media/vocal/zundamon/vspin.ogg b/media/vocal/zundamon/vspin.ogg new file mode 100644 index 000000000..00de8914a Binary files /dev/null and b/media/vocal/zundamon/vspin.ogg differ diff --git a/media/vocal/zundamon/vspin0.ogg b/media/vocal/zundamon/vspin0.ogg new file mode 100644 index 000000000..04f8be13a Binary files /dev/null and b/media/vocal/zundamon/vspin0.ogg differ diff --git a/media/vocal/zundamon/welcome_1.ogg b/media/vocal/zundamon/welcome_1.ogg new file mode 100644 index 000000000..4dec925e3 Binary files /dev/null and b/media/vocal/zundamon/welcome_1.ogg differ diff --git a/media/vocal/zundamon/welcome_2.ogg b/media/vocal/zundamon/welcome_2.ogg new file mode 100644 index 000000000..06005923c Binary files /dev/null and b/media/vocal/zundamon/welcome_2.ogg differ diff --git a/media/vocal/zundamon/win_1.ogg b/media/vocal/zundamon/win_1.ogg new file mode 100644 index 000000000..a675f27e5 Binary files /dev/null and b/media/vocal/zundamon/win_1.ogg differ diff --git a/media/vocal/zundamon/win_2.ogg b/media/vocal/zundamon/win_2.ogg new file mode 100644 index 000000000..4b7cb0f7d Binary files /dev/null and b/media/vocal/zundamon/win_2.ogg differ diff --git a/media/vocal/zundamon/wspin.ogg b/media/vocal/zundamon/wspin.ogg new file mode 100644 index 000000000..8812ccd10 Binary files /dev/null and b/media/vocal/zundamon/wspin.ogg differ diff --git a/media/vocal/zundamon/wspin0.ogg b/media/vocal/zundamon/wspin0.ogg new file mode 100644 index 000000000..656c51ccc Binary files /dev/null and b/media/vocal/zundamon/wspin0.ogg differ diff --git a/media/vocal/zundamon/xspin.ogg b/media/vocal/zundamon/xspin.ogg new file mode 100644 index 000000000..fdfc779d2 Binary files /dev/null and b/media/vocal/zundamon/xspin.ogg differ diff --git a/media/vocal/zundamon/xspin0.ogg b/media/vocal/zundamon/xspin0.ogg new file mode 100644 index 000000000..3314cb452 Binary files /dev/null and b/media/vocal/zundamon/xspin0.ogg differ diff --git a/media/vocal/zundamon/yspin.ogg b/media/vocal/zundamon/yspin.ogg new file mode 100644 index 000000000..da2e0ae54 Binary files /dev/null and b/media/vocal/zundamon/yspin.ogg differ diff --git a/media/vocal/zundamon/yspin0.ogg b/media/vocal/zundamon/yspin0.ogg new file mode 100644 index 000000000..4362b18e8 Binary files /dev/null and b/media/vocal/zundamon/yspin0.ogg differ diff --git a/media/vocal/zundamon/zspin.ogg b/media/vocal/zundamon/zspin.ogg new file mode 100644 index 000000000..5dc2ecbc7 Binary files /dev/null and b/media/vocal/zundamon/zspin.ogg differ diff --git a/media/vocal/zundamon/zspin0.ogg b/media/vocal/zundamon/zspin0.ogg new file mode 100644 index 000000000..543951dde Binary files /dev/null and b/media/vocal/zundamon/zspin0.ogg differ diff --git a/parts/RSlist.lua b/parts/RSlist.lua index cd3500bdc..14a4e0688 100644 --- a/parts/RSlist.lua +++ b/parts/RSlist.lua @@ -1,43 +1,43 @@ -local defaultCenterTex=GC.DO{1,1}--No texture -local defaultCenterPos={--For SRS-like RSs - --Tetromino - {[0]={0,1},{1,0},{1,1},{1,1}},--Z - {[0]={0,1},{1,0},{1,1},{1,1}},--S - {[0]={0,1},{1,0},{1,1},{1,1}},--J - {[0]={0,1},{1,0},{1,1},{1,1}},--L - {[0]={0,1},{1,0},{1,1},{1,1}},--T - {[0]={.5,.5},{.5,.5},{.5,.5},{.5,.5}},--O - {[0]={-.5,1.5},{1.5,-.5},{.5,1.5},{1.5,.5}},--I +local defaultCenterTex=GC.DO{1,1}-- No texture +local defaultCenterPos={-- For SRS-like RSs + -- Tetromino + {[0]={0,1},{1,0},{1,1},{1,1}},-- Z + {[0]={0,1},{1,0},{1,1},{1,1}},-- S + {[0]={0,1},{1,0},{1,1},{1,1}},-- J + {[0]={0,1},{1,0},{1,1},{1,1}},-- L + {[0]={0,1},{1,0},{1,1},{1,1}},-- T + {[0]={.5,.5},{.5,.5},{.5,.5},{.5,.5}},-- O + {[0]={-.5,1.5},{1.5,-.5},{.5,1.5},{1.5,.5}},-- I - --Pentomino - {[0]={1,1},{1,1},{1,1},{1,1}},--Z5 - {[0]={1,1},{1,1},{1,1},{1,1}},--S5 - {[0]={0,1},{1,0},{1,1},{1,1}},--P - {[0]={0,1},{1,0},{1,1},{1,1}},--Q - {[0]={1,1},{1,1},{1,1},{1,1}},--F - {[0]={1,1},{1,1},{1,1},{1,1}},--E - {[0]={1,1},{1,1},{1,1},{1,1}},--T5 - {[0]={0,1},{1,0},{1,1},{1,1}},--U - {[0]={.5,1.5},{.5,.5},{1.5,.5},{1.5,1.5}},--V - {[0]={1,1},{1,1},{1,1},{1,1}},--W - {[0]={1,1},{1,1},{1,1},{1,1}},--X - {[0]={.5,1.5},{1.5,.5},{.5,1.5},{1.5,.5}},--J5 - {[0]={.5,1.5},{1.5,.5},{.5,1.5},{1.5,.5}},--L5 - {[0]={.5,1.5},{1.5,.5},{.5,1.5},{1.5,.5}},--R - {[0]={.5,1.5},{1.5,.5},{.5,1.5},{1.5,.5}},--Y - {[0]={.5,1.5},{1.5,.5},{.5,1.5},{1.5,.5}},--N - {[0]={.5,1.5},{1.5,.5},{.5,1.5},{1.5,.5}},--H - {[0]={0,2},{2,0},{0,2},{2,0}},--I5 + -- Pentomino + {[0]={1,1},{1,1},{1,1},{1,1}},-- Z5 + {[0]={1,1},{1,1},{1,1},{1,1}},-- S5 + {[0]={0,1},{1,0},{1,1},{1,1}},-- P + {[0]={0,1},{1,0},{1,1},{1,1}},-- Q + {[0]={1,1},{1,1},{1,1},{1,1}},-- F + {[0]={1,1},{1,1},{1,1},{1,1}},-- E + {[0]={1,1},{1,1},{1,1},{1,1}},-- T5 + {[0]={0,1},{1,0},{1,1},{1,1}},-- U + {[0]={.5,1.5},{.5,.5},{1.5,.5},{1.5,1.5}},-- V + {[0]={1,1},{1,1},{1,1},{1,1}},-- W + {[0]={1,1},{1,1},{1,1},{1,1}},-- X + {[0]={.5,1.5},{1.5,.5},{.5,1.5},{1.5,.5}},-- J5 + {[0]={.5,1.5},{1.5,.5},{.5,1.5},{1.5,.5}},-- L5 + {[0]={.5,1.5},{1.5,.5},{.5,1.5},{1.5,.5}},-- R + {[0]={.5,1.5},{1.5,.5},{.5,1.5},{1.5,.5}},-- Y + {[0]={.5,1.5},{1.5,.5},{.5,1.5},{1.5,.5}},-- N + {[0]={.5,1.5},{1.5,.5},{.5,1.5},{1.5,.5}},-- H + {[0]={0,2},{2,0},{0,2},{2,0}},-- I5 - --Trimino - {[0]={0,1},{1,0},{0,1},{1,0}},--I3 - {[0]={.5,.5},{.5,.5},{.5,.5},{.5,.5}},--C + -- Trimino + {[0]={0,1},{1,0},{0,1},{1,0}},-- I3 + {[0]={.5,.5},{.5,.5},{.5,.5},{.5,.5}},-- C - --Domino - {[0]={-.5,.5},{.5,-.5},{.5,.5},{.5,.5}},--I2 + -- Domino + {[0]={-.5,.5},{.5,-.5},{.5,.5},{.5,.5}},-- I2 - --Dot - {[0]={0,0},{0,0},{0,0},{0,0}},--O1 + -- Dot + {[0]={0,0},{0,0},{0,0},{0,0}},-- O1 } local noKickSet,noKickSet_180 do @@ -52,14 +52,14 @@ local function _strToVec(list) return list end ---Use this if the block is centrosymmetry, *PTR!!! +-- Use this if the block is centrosymmetry, *PTR!!! local function _centroSymSet(L) L[23]=L[01]L[32]=L[10] L[21]=L[03]L[12]=L[30] L[20]=L[02]L[31]=L[13] end ---Use this to copy a symmetry set +-- Use this to copy a symmetry set local function _flipList(O) if not O then return @@ -72,7 +72,7 @@ local function _flipList(O) end local function _reflect(a) - return{ + return { [03]=_flipList(a[01]), [01]=_flipList(a[03]), [30]=_flipList(a[10]), @@ -91,21 +91,21 @@ end local TRS do local OspinList={ - {111,5,2, 0,-1,0},{111,5,2,-1,-1,0},{111,5,0,-1, 0,0},--T - {333,5,2,-1,-1,0},{333,5,2, 0,-1,0},{333,5,0, 0, 0,0},--T - {313,1,2,-1, 0,0},{313,1,2, 0,-1,0},{313,1,2, 0, 0,0},--Z - {131,2,2, 0, 0,0},{131,2,2,-1,-1,0},{131,2,2,-1, 0,0},--S - {131,1,2,-1, 0,0},{131,1,2, 0,-1,0},{131,1,2, 0, 0,0},--Z(misOrder) - {313,2,2, 0, 0,0},{313,2,2,-1,-1,0},{313,2,2,-1, 0,0},--S(misOrder) - {331,3,2, 0,-1,0},--J(farDown) - {113,4,2,-1,-1,0},--L(farDown) - {113,3,2,-1,-1,0},{113,3,0, 0, 0,0},--J - {331,4,2, 0,-1,0},{331,4,0,-1, 0,0},--L - {222,7,0,-1, 1,1},{222,7,0,-2, 1,1},{222,7,0, 0, 1,1},--I(high) - {222,7,2,-1, 0,2},{222,7,2,-2, 0,2},{222,7,2, 0, 0,2},--I(low) - {121,6,0, 1,-1,2},{112,6,0, 2,-1,2},{122,6,0, 1,-2,2},--O - {323,6,0,-1,-1,2},{332,6,0,-2,-1,2},{322,6,0,-1,-2,2},--O - }--{keys, ID, dir, dx, dy, freeLevel (0=immovable, 1=U/D-immovable, 2=free)} + {111,5,2, 0,-1,0},{111,5,2,-1,-1,0},{111,5,0,-1, 0,0},-- T + {333,5,2,-1,-1,0},{333,5,2, 0,-1,0},{333,5,0, 0, 0,0},-- T + {313,1,2,-1, 0,0},{313,1,2, 0,-1,0},{313,1,2, 0, 0,0},-- Z + {131,2,2, 0, 0,0},{131,2,2,-1,-1,0},{131,2,2,-1, 0,0},-- S + {131,1,2,-1, 0,0},{131,1,2, 0,-1,0},{131,1,2, 0, 0,0},-- Z(misOrder) + {313,2,2, 0, 0,0},{313,2,2,-1,-1,0},{313,2,2,-1, 0,0},-- S(misOrder) + {331,3,2, 0,-1,0},-- J(farDown) + {113,4,2,-1,-1,0},-- L(farDown) + {113,3,2,-1,-1,0},{113,3,0, 0, 0,0},-- J + {331,4,2, 0,-1,0},{331,4,0,-1, 0,0},-- L + {222,7,0,-1, 1,1},{222,7,0,-2, 1,1},{222,7,0, 0, 1,1},-- I(high) + {222,7,2,-1, 0,2},{222,7,2,-2, 0,2},{222,7,2, 0, 0,2},-- I(low) + {121,6,0, 1,-1,2},{112,6,0, 2,-1,2},{122,6,0, 1,-2,2},-- O + {323,6,0,-1,-1,2},{332,6,0,-2,-1,2},{322,6,0,-1,-2,2},-- O + }-- {keys, ID, dir, dx, dy, freeLevel (0=immovable, 1=U/D-immovable, 2=free)} local XspinList={ {{ 1,-1},{ 1, 0},{ 1, 1},{ 1,-2},{ 1, 2}}, {{ 0,-1},{ 0,-2},{ 0, 1},{ 0,-2},{ 0, 2}}, @@ -126,7 +126,7 @@ do kickTable={ { [01]={'+0+0','-1+0','-1+1','+0-2','-1+2','+0+1'}, - [10]={'+0+0','+1+0','+1-1','+0+2','+1-2','+1-2'}, + [10]={'+0+0','+1+0','+1-1','+0+2','+1-2','+0-1'}, [03]={'+0+0','+1+0','+1+1','+0-2','+1-1','+1-2'}, [30]={'+0+0','-1+0','-1-1','+0+2','-1+2','+0-1'}, [12]={'+0+0','+1+0','+1-1','+0+2','+1+2','+1+1'}, @@ -137,8 +137,8 @@ do [20]={'+0+0','-1+0','+1+0','+0+1','+0-1'}, [13]={'+0+0','+0-1','+0+1','+0-2'}, [31]={'+0+0','+0+1','+0-1','+0+2'}, - },--Z - false,--S + },-- Z + false,-- S { [01]={'+0+0','-1+0','-1+1','+0-2','+1+1','+0+1','+0-1'}, [10]={'+0+0','+1+0','+1-1','+0+2','-1-1','+0-1','+0+1'}, @@ -152,30 +152,30 @@ do [20]={'+0+0','+1+0','-1+0','+0+1','+0-1'}, [13]={'+0+0','+0-1','+0+1','+1+0'}, [31]={'+0+0','+0+1','+0-1','-1+0'}, - },--J - false,--L + },-- J + false,-- L { [01]={'+0+0','-1+0','-1+1','+0-2','-1-2','+0+1'}, [10]={'+0+0','+1+0','+1-1','+0+2','+1+2','+0+1','+0-1'}, [03]={'+0+0','+1+0','+1+1','+0-2','+1-2','+0+1'}, [30]={'+0+0','-1+0','-1-1','+0+2','-1+2','+0+1','+0-1'}, [12]={'+0+0','+1+0','+1-1','+0-1','-1-1','+0+2','+1+2','+1+1'}, - [21]={'+0+0','-1+0','+0-2','-1-2','-1-1','+1+1'}, + [21]={'+0+0','-1+0','+0-2','-1-2','-1-1','+0-1','+1+1'}, [32]={'+0+0','-1+0','-1-1','+0-1','+1-1','+0+2','-1+2','-1+1'}, - [23]={'+0+0','+1+0','+0-2','+1-2','+1-1','-1+1'}, + [23]={'+0+0','+1+0','+0-2','+1-2','+1-1','+0-1','-1+1'}, [02]={'+0+0','-1+0','+1+0','+0+1'}, [20]={'+0+0','+1+0','-1+0','+0-1'}, [13]={'+0+0','+0-1','+0+1','+1+0','+0-2','+0+2'}, [31]={'+0+0','+0-1','+0+1','-1+0','+0-2','+0+2'}, - },--T + },-- T function(P,d) if P.gameEnv.easyFresh then - P:freshBlock('fresh') + P:freshBlockDelay() end if P.gameEnv.ospin then local x,y=P.curX,P.curY local C=P.cur - if y==P.ghoY and((P:solid(x-1,y)or P:solid(x-1,y+1)))and(P:solid(x+2,y)or P:solid(x+2,y+1))then + if y==P.ghoY and ((P:solid(x-1,y) or P:solid(x-1,y+1))) and (P:solid(x+2,y) or P:solid(x+2,y+1)) then if P.sound then SFX.play('rotatekick',nil,P:getCenterX()*.15) end @@ -186,15 +186,15 @@ do return end for i=1,#OspinList do local L=OspinList[i] - if C.spinSeq==L[1]then + if C.spinSeq==L[1] then local id,dir=L[2],L[3] local bk=BLOCKS[id][dir] x,y=P.curX+L[4],P.curY+L[5] if - not P:ifoverlap(bk,x,y)and( - L[6]>0 or(P:ifoverlap(bk,x-1,y)and P:ifoverlap(bk,x+1,y)) - )and( - L[6]==2 or(P:ifoverlap(bk,x,y-1)and P:ifoverlap(bk,x,y+1)) + not P:ifoverlap(bk,x,y) and ( + L[6]>0 or (P:ifoverlap(bk,x-1,y) and P:ifoverlap(bk,x+1,y)) + ) and ( + L[6]==2 or (P:ifoverlap(bk,x,y-1) and P:ifoverlap(bk,x,y+1)) ) then C.id=id @@ -203,7 +203,7 @@ do C.dir=dir P.spinLast=2 P.stat.rotate=P.stat.rotate+1 - P:freshBlock('move') + P:freshMoveBlock() C.spinSeq=nil return end @@ -220,7 +220,7 @@ do SFX.play('rotate',nil,P:getCenterX()*.15) end end - end,--O + end,-- O { [01]={'+0+0','+0+1','+1+0','-2+0','-2-1','+1+2'}, [10]={'+0+0','+2+0','-1+0','-1-2','+2+1','+0+1'}, @@ -234,14 +234,14 @@ do [20]={'+0+0','+1+0','-1+0','+0+1','+0-1'}, [13]={'+0+0','+0-1','-1+0','+1+0','+0+1'}, [31]={'+0+0','+0-1','+1+0','-1+0','+0+1'}, - },--I + },-- I { [01]={'+0+0','+0+1','+1+1','-1+0','+0-3','+0+2','+0-2','+0+3','-1+2'}, [10]={'+0+0','+0-1','-1-1','+1+0','+0-3','+0+2','+0-2','+0+3','+1-2'}, [03]={'+0+0','+1+0','+0-3','+0-1','+0+1','+0-2','+0+2','+0+3','+1+2'}, [30]={'+0+0','-1+0','+0-1','+0+1','+0-2','+0-3','+0+2','+0+3','-1-2'}, - },--Z5 - false,--S5 + },-- Z5 + false,-- S5 { [01]={'+0+0','-1+0','-1+1','+0-2','-1-2','-1-1','+0+1'}, [10]={'+0+0','+1+0','+1-1','+0+2','+1+2','+0-1','+1+1'}, @@ -255,8 +255,8 @@ do [20]={'+0+0','+1+0','+0+1','+0-1'}, [13]={'+0+0','+1+0','+0+1','-1+0'}, [31]={'+0+0','-1+0','+0-1','+1+0'}, - },--P - false,--Q + },-- P + false,-- Q { [01]={'+0+0','-1+0','+1+0','-1+1','+0-2','+0-3'}, [10]={'+0+0','+1+0','+1-1','-1+0','+0+2','+0+3'}, @@ -270,8 +270,8 @@ do [20]={'+0+0','-1+0','+1+0','+1+1'}, [13]={'+0+0','+0-1','-1+1','+0+1'}, [31]={'+0+0','+0-1','+1-1','+0+1'}, - },--F - false,--E + },-- F + false,-- E { [01]={'+0+0','+0-1','-1-1','+1+0','+1+1','+0-3','-1+0','+0+2','-1+2'}, [10]={'+0+0','+1+0','+0-1','-1-1','+0-2','-1+1','+0-3','+1-2','+0+1'}, @@ -285,7 +285,7 @@ do [20]={'+0+0','+0-1','+0+1','+0-2'}, [13]={'+0+0','+1+0','-1+1','-2+0'}, [31]={'+0+0','-1+0','+1+1','+2+0'}, - },--T5 + },-- T5 { [01]={'+0+0','-1+0','-1+1','+0-2','-1-2'}, [10]={'+0+0','+1+0','+1-1','+0+2','+1+2'}, @@ -299,7 +299,7 @@ do [20]={'+0+0','+0-1'}, [13]={'+0+0','+0-1','+0+1','+1+0'}, [31]={'+0+0','+0-1','+0+1','-1+0'}, - },--U + },-- U { [01]={'+0+0','+0+1','-1+0','+0-2','-1-2'}, [10]={'+0+0','+0+1','+1+0','+0-2','+1-2'}, @@ -313,7 +313,7 @@ do [20]={'+0+0','+1-1','-1+1'}, [13]={'+0+0','+1+1','-1-1'}, [31]={'+0+0','-1-1','+1+1'}, - },--V + },-- V { [01]={'+0+0','+0-1','-1+0','+1+0','+1-1','+0+2'}, [10]={'+0+0','+0-1','-1-1','+0+1','+0-2','+1-2','+0+2'}, @@ -327,24 +327,21 @@ do [20]={'+0+0','+0+1','+1+0'}, [13]={'+0+0','+0+1','-1+0'}, [31]={'+0+0','+0-1','+1+0'}, - },--W - function(P,d) - if P.type=='human'then - SFX.play('rotate',nil,P:getCenterX()*.15) - end - local kickData=XspinList[d] - for test=1,#kickData do - local x,y=P.curX+kickData[test][1],P.curY+kickData[test][2] - if not P:ifoverlap(P.cur.bk,x,y)then - P.curX,P.curY=x,y - P.spinLast=1 - P:freshBlock('move') - P.stat.rotate=P.stat.rotate+1 - return - end - end - P:freshBlock('fresh') - end,--X + },-- W + { + [01]=XspinList[1], + [12]=XspinList[1], + [23]=XspinList[1], + [30]=XspinList[1], + [02]=XspinList[2], + [13]=XspinList[2], + [20]=XspinList[2], + [31]=XspinList[2], + [03]=XspinList[3], + [10]=XspinList[3], + [21]=XspinList[3], + [32]=XspinList[3], + },-- X { [01]={'+0+0','-1+0','-1+1','+0-3','-1+1','-1+2','+0+1'}, [10]={'+0+0','-1+0','+1-1','+0+3','+1-1','+1-2','+0+1'}, @@ -358,8 +355,8 @@ do [20]={'+0+0','+0+1','+1+1','-1+1','+1+0','-2+1'}, [13]={'+0+0','-1+0','-1-1','+0+1','-1-2'}, [31]={'+0+0','+1+0','+1+1','+0-1','+1+2'}, - },--J5 - false,--L5 + },-- J5 + false,-- L5 { [01]={'+0+0','-1+0','-1+1','+1+0','-1+2','-1-1','+0-3','+0+1'}, [10]={'+0+0','-1+0','+1+0','+1-1','+1-2','+1+1','+0+3','+0+1'}, @@ -373,8 +370,8 @@ do [20]={'+0+0','+0+1','-1+1','+1+0','-2+1','+0-1'}, [13]={'+0+0','-1+0','-1-1','+0+1','-1-2'}, [31]={'+0+0','+1+0','+1+1','+0-1','+1+2'}, - },--R - false,--Y + },-- R + false,-- Y { [01]={'+0+0','-1+0','-1+1','+0+1','+1+0','+1+1','-1+2','-2+0','+0-2'}, [10]={'+0+0','+1+0','-1+0','+0-1','-1-1','+1-1','+1-2','+2+0','+0+2'}, @@ -388,20 +385,20 @@ do [20]={'+0+0','+1+0','+0-2','+0+1'}, [13]={'+0+0','-1+0','-1-1','+0+1','+1+2'}, [31]={'+0+0','+1+0','+1+1','+0-1','-1-2'}, - },--N - false,--H + },-- N + false,-- H { [01]={'+0+0','+1-1','+1+0','+1+1','+0+1','-1+1','-1+0','-1-1','+0-1','+0-2','-2-1','-2-2','+2+0','+2-1','+2-2','+1+2','+2+2','-1+2','-2+2'}, [10]={'+0+0','-1+0','-1-1','+0-1','+1-1','-2-2','-2-1','-2+0','-1-2','+0-2','+1-2','+2-2','-1+1','-2+1','-2+2','+1+0','+2+0','+2-1','+0+1','+1-1','+2-2'}, - [03]={'+0+0','-1-1','-1+0','-1+1','-0+1','+1+1','+1+0','+1-1','-0-1','-0-2','+2-1','+2-2','-2+0','-2-1','-2-2','-1+2','-2+2','+1+2','+2+2'}, - [30]={'+0+0','+1+0','+1-1','-0-1','-1-1','+2-2','+2-1','+2+0','+1-2','-0-2','-1-2','-2-2','+1+1','+2+1','+2+2','-1+0','-2+0','-2-1','+0+1','-1-1','-2-2'}, - },--I5 + [03]={'+0+0','-1-1','-1+0','-1+1','+0+1','+1+1','+1+0','+1-1','+0-1','+0-2','+2-1','+2-2','-2+0','-2-1','-2-2','-1+2','-2+2','+1+2','+2+2'}, + [30]={'+0+0','+1+0','+1-1','+0-1','-1-1','+2-2','+2-1','+2+0','+1-2','+0-2','-1-2','-2-2','+1+1','+2+1','+2+2','-1+0','-2+0','-2-1','+0+1','-1-1','-2-2'}, + },-- I5 { [01]={'+0+0','-1+0','-1-1','+1+1','-1+1'}, [10]={'+0+0','-1+0','+1+0','-1-1','+1+1'}, [03]={'+0+0','+1+0','+1-1','-1+1','+1+1'}, [30]={'+0+0','+1+0','-1+0','+1-1','-1+1'}, - },--I3 + },-- I3 { [01]={'+0+0','-1+0','+1+0'}, [10]={'+0+0','+1+0','-1+0'}, @@ -415,7 +412,7 @@ do [20]={'+0+0','+0+1','-1+1','+1+1'}, [13]={'+0+0','+0-1','-1-1','+1-1'}, [31]={'+0+0','+0+1','+1+1','-1+1'}, - },--C + },-- C { [01]={'+0+0','-1+0','+0+1'}, [10]={'+0+0','+1+0','+0+1'}, @@ -429,25 +426,25 @@ do [20]={'+0+0','+0+1','+0-1'}, [13]={'+0+0','-1+0','+1+0'}, [31]={'+0+0','+1+0','-1+0'}, - },--I2 - nil,--O1 + },-- I2 + nil,-- O1 } } TRS.centerDisp[6]=false TRS.centerDisp[18]=false - TRS.kickTable[2]= _reflect(TRS.kickTable[1])--SZ - TRS.kickTable[4]= _reflect(TRS.kickTable[3])--LJ - TRS.kickTable[9]= _reflect(TRS.kickTable[8])--S5Z5 - TRS.kickTable[11]=_reflect(TRS.kickTable[10])--PQ - TRS.kickTable[13]=_reflect(TRS.kickTable[12])--FE - TRS.kickTable[20]=_reflect(TRS.kickTable[19])--L5J5 - TRS.kickTable[22]=_reflect(TRS.kickTable[21])--RY - TRS.kickTable[24]=_reflect(TRS.kickTable[23])--NH - _centroSymSet(TRS.kickTable[8])_centroSymSet(TRS.kickTable[9])--S5Z5 - _centroSymSet(TRS.kickTable[25])_centroSymSet(TRS.kickTable[26])--I5I3 + TRS.kickTable[2]= _reflect(TRS.kickTable[1])-- SZ + TRS.kickTable[4]= _reflect(TRS.kickTable[3])-- LJ + TRS.kickTable[9]= _reflect(TRS.kickTable[8])-- S5Z5 + TRS.kickTable[11]=_reflect(TRS.kickTable[10])-- PQ + TRS.kickTable[13]=_reflect(TRS.kickTable[12])-- FE + TRS.kickTable[20]=_reflect(TRS.kickTable[19])-- L5J5 + TRS.kickTable[22]=_reflect(TRS.kickTable[21])-- RY + TRS.kickTable[24]=_reflect(TRS.kickTable[23])-- NH + _centroSymSet(TRS.kickTable[8])_centroSymSet(TRS.kickTable[9])-- S5Z5 + _centroSymSet(TRS.kickTable[25])_centroSymSet(TRS.kickTable[26])-- I5I3 end -local SRS +local SRS -- Pentos are treated as 3*3 piece do SRS={ centerTex=GC.DO{10,10, @@ -469,12 +466,12 @@ do [32]={'+0+0','-1+0','-1-1','+0+2','-1+2'}, [23]={'+0+0','+1+0','+1+1','+0-2','+1-2'}, [02]={'+0+0'},[20]={'+0+0'},[13]={'+0+0'},[31]={'+0+0'}, - },--Z - false,--S - false,--J - false,--L - false,--T - noKickSet,--O + },-- Z + false,-- S + false,-- J + false,-- L + false,-- T + noKickSet,-- O { [01]={'+0+0','-2+0','+1+0','-2-1','+1+2'}, [10]={'+0+0','+2+0','-1+0','+2+1','-1-2'}, @@ -485,14 +482,14 @@ do [30]={'+0+0','+1+0','-2+0','+1-2','-2+1'}, [03]={'+0+0','-1+0','+2+0','-1+2','+2-1'}, [02]={'+0+0'},[20]={'+0+0'},[13]={'+0+0'},[31]={'+0+0'}, - }--I + }-- I } } - for i=2,5 do SRS.kickTable[i]=SRS.kickTable[1]end - for i=8,29 do SRS.kickTable[i]=SRS.kickTable[1]end + for i=2,5 do SRS.kickTable[i]=SRS.kickTable[1] end + for i=8,29 do SRS.kickTable[i]=SRS.kickTable[1] end end -local SRS_plus +local SRS_plus -- Basic 180 kicks and symmetry I piece kick table do SRS_plus={ centerTex=GC.DO{10,10, @@ -519,33 +516,33 @@ do [20]={'+0+0','+1+0','-1+0','+0-1','+0+1'}, [13]={'+0+0','+0-1','-1+0','+1+0','+0+1'}, [31]={'+0+0','+0-1','-1+0','+1+0','+0+1'}, - },--Z - false,--S - false,--J - false,--L - false,--T - noKickSet,--O + },-- Z + false,-- S + false,-- J + false,-- L + false,-- T + noKickSet,-- O { - [01]={'+0+0','-2+0','+1+0','-2-1','+1+2'}, - [10]={'+0+0','+2+0','-1+0','+2+1','-1-2'}, + [01]={'+0+0','+1+0','-2+0','+1+2','-2-1'}, + [10]={'+0+0','-1+0','+2+0','-1-2','+2+1'}, + [03]={'+0+0','-1+0','+2+0','-1+2','+2-1'}, + [30]={'+0+0','+1+0','-2+0','+1-2','-2+1'}, [12]={'+0+0','-1+0','+2+0','-1+2','+2-1'}, [21]={'+0+0','+1+0','-2+0','+1-2','-2+1'}, - [23]={'+0+0','+2+0','-1+0','+2+1','-1-2'}, - [32]={'+0+0','-2+0','+1+0','-2-1','+1+2'}, - [30]={'+0+0','+1+0','-2+0','+1-2','-2+1'}, - [03]={'+0+0','-1+0','+2+0','-1+2','+2-1'}, + [32]={'+0+0','+1+0','-2+0','+1+2','-2-1'}, + [23]={'+0+0','-1+0','+2+0','-1-2','+2+1'}, [02]={'+0+0','-1+0','+1+0','+0-1','+0+1'}, [20]={'+0+0','+1+0','-1+0','+0+1','+0-1'}, [13]={'+0+0','+0-1','-1+0','+1+0','+0+1'}, [31]={'+0+0','+0-1','+1+0','-1+0','+0+1'}, - }--I + }-- I } } - for i=2,5 do SRS_plus.kickTable[i]=SRS_plus.kickTable[1]end - for i=8,29 do SRS_plus.kickTable[i]=SRS_plus.kickTable[1]end + for i=2,5 do SRS_plus.kickTable[i]=SRS_plus.kickTable[1] end + for i=8,29 do SRS_plus.kickTable[i]=SRS_plus.kickTable[1] end end -local SRS_X +local SRS_X -- Basic 180 kicks and TRS for non-SZJLT do SRS_X={ centerTex=GC.DO{10,10, @@ -570,19 +567,19 @@ do [20]={'+0+0','+1+0','-1+0','+0-1','+0+1'}, [13]={'+0+0','+0-1','-1+0','+1+0','+0+1'}, [31]={'+0+0','+0-1','-1+0','+1+0','+0+1'}, - },--Z - false,--S - false,--J - false,--L - false,--T - noKickSet,--O - false,--I + },-- Z + false,-- S + false,-- J + false,-- L + false,-- T + noKickSet,-- O + false,-- I } } - for i=2,5 do SRS_X.kickTable[i]=SRS_X.kickTable[1]end - for i=7,29 do SRS_X.kickTable[i]=TRS.kickTable[i]end - SRS_X.kickTable[10]=SRS_X.kickTable[1]--P - SRS_X.kickTable[11]=SRS_X.kickTable[1]--Q + for i=2,5 do SRS_X.kickTable[i]=SRS_X.kickTable[1] end + for i=7,29 do SRS_X.kickTable[i]=TRS.kickTable[i] end + SRS_X.kickTable[10]=SRS_X.kickTable[1]-- P + SRS_X.kickTable[11]=SRS_X.kickTable[1]-- Q end local BiRS @@ -591,46 +588,54 @@ do local L=_strToVec{'+0+0','+1+0','+1-1','+0-1','+1+1','-1-1','-1+0','+0+1','-1+1','+0+2','+1+2','-1+2','+2+0','-2+0'} local F=_strToVec{'+0+0','+0-1','+0+1','+0+2'} local list={ - {[02]=L,[20]=R,[13]=R,[31]=L},--Z - {[02]=R,[20]=L,[13]=L,[31]=R},--S - {[02]=L,[20]=R,[13]=L,[31]=R},--J - {[02]=R,[20]=L,[13]=L,[31]=R},--L - {[02]=F,[20]=F,[13]=L,[31]=R},--T - {[02]=F,[20]=F,[13]=F,[31]=F},--O - {[02]=F,[20]=F,[13]=R,[31]=L},--I + {[02]=L,[20]=R,[13]=R,[31]=L},-- Z + {[02]=R,[20]=L,[13]=R,[31]=L},-- S + {[02]=L,[20]=R,[13]=L,[31]=R},-- J + {[02]=R,[20]=L,[13]=L,[31]=R},-- L + {[02]=F,[20]=F,[13]=L,[31]=R},-- T + {[02]=F,[20]=F,[13]=F,[31]=F},-- O + {[02]=F,[20]=F,[13]=R,[31]=L},-- I - {[02]=L,[20]=L,[13]=R,[31]=R},--Z5 - {[02]=R,[20]=R,[13]=L,[31]=L},--S5 - {[02]=L,[20]=R,[13]=L,[31]=R},--P - {[02]=R,[20]=L,[13]=R,[31]=L},--Q - {[02]=R,[20]=L,[13]=L,[31]=R},--F - {[02]=L,[20]=R,[13]=R,[31]=L},--E - {[02]=F,[20]=F,[13]=L,[31]=R},--T5 - {[02]=F,[20]=F,[13]=L,[31]=R},--U - {[02]=R,[20]=L,[13]=L,[31]=R},--V - {[02]=R,[20]=L,[13]=L,[31]=R},--W - {[02]=F,[20]=F,[13]=F,[31]=F},--X - {[02]=L,[20]=R,[13]=R,[31]=L},--J5 - {[02]=R,[20]=L,[13]=L,[31]=R},--L5 - {[02]=L,[20]=R,[13]=R,[31]=L},--R - {[02]=R,[20]=L,[13]=L,[31]=R},--Y - {[02]=L,[20]=R,[13]=R,[31]=L},--N - {[02]=R,[20]=L,[13]=L,[31]=R},--H - {[02]=F,[20]=F,[13]=F,[31]=F},--I5 + {[02]=L,[20]=L,[13]=R,[31]=R},-- Z5 + {[02]=R,[20]=R,[13]=L,[31]=L},-- S5 + {[02]=L,[20]=R,[13]=L,[31]=R},-- P + {[02]=R,[20]=L,[13]=L,[31]=R},-- Q + {[02]=R,[20]=L,[13]=R,[31]=L},-- F + {[02]=L,[20]=R,[13]=R,[31]=L},-- E + {[02]=F,[20]=F,[13]=L,[31]=R},-- T5 + {[02]=F,[20]=F,[13]=L,[31]=R},-- U + {[02]=R,[20]=L,[13]=L,[31]=R},-- V + {},-- W + {[02]=F,[20]=F,[13]=F,[31]=F},-- X + {[02]=L,[20]=R,[13]=L,[31]=R},-- J5 + {[02]=R,[20]=L,[13]=L,[31]=R},-- L5 + {[02]=L,[20]=R,[13]=L,[31]=R},-- R + {[02]=R,[20]=L,[13]=L,[31]=R},-- Y + {[02]=L,[20]=R,[13]=R,[31]=L},-- N + {[02]=R,[20]=L,[13]=R,[31]=L},-- H + {[02]=F,[20]=F,[13]=F,[31]=F},-- I5 - {[02]=F,[20]=F,[13]=F,[31]=F},--I3 - {[02]=R,[20]=L,[13]=L,[31]=R},--C - {[02]=F,[20]=F,[13]=R,[31]=L},--I2 - {[02]=F,[20]=F,[13]=F,[31]=F},--O1 + {[02]=F,[20]=F,[13]=F,[31]=F},-- I3 + {[02]=R,[20]=L,[13]=L,[31]=R},-- C + {[02]=F,[20]=F,[13]=R,[31]=L},-- I2 + {[02]=F,[20]=F,[13]=F,[31]=F},-- O1 } for i=1,29 do local a,b=R,L if i==6 or i==18 then a,b=b,a end - list[i][01]=a;list[i][10]=b;list[i][03]=b;list[i][30]=a - list[i][12]=a;list[i][21]=b;list[i][32]=b;list[i][23]=a + list[i][01]=a; list[i][10]=b; list[i][03]=b; list[i][30]=a + list[i][12]=a; list[i][21]=b; list[i][32]=b; list[i][23]=a end + list[17]={ -- Fix W + [01]=L,[32]=R, + [03]=L,[30]=R, + [10]=R,[23]=L, + [12]=L,[21]=R, + [02]=R,[20]=L, + [31]=L,[13]=R, + } BiRS={ centerTex=GC.DO{10,10, {'setCL',1,1,1,.6}, @@ -653,9 +658,9 @@ do end local dx,dy=0,0 do local pressing=P.keyPressing - if pressing[1]and P:ifoverlap(C.bk,P.curX-1,P.curY)then dx=dx-1 end - if pressing[2]and P:ifoverlap(C.bk,P.curX+1,P.curY)then dx=dx+1 end - if pressing[7]and P:ifoverlap(C.bk,P.curX,P.curY-1)then dy= -1 end + if pressing[1] and P:ifoverlap(C.bk,P.curX-1,P.curY) then dx=dx-1 end + if pressing[2] and P:ifoverlap(C.bk,P.curX+1,P.curY) then dx=dx+1 end + if pressing[7] and P:ifoverlap(C.bk,P.curX,P.curY-1) then dy= -1 end end while true do for test=1,#kickList do @@ -666,7 +671,7 @@ do (P.freshTime>0 or fdy<=0) then local x,y=ix+fdx,iy+fdy - if not P:ifoverlap(icb,x,y)then + if not P:ifoverlap(icb,x,y) then if P.gameEnv.moveFX and P.gameEnv.block then P:createMoveFX() end @@ -676,7 +681,7 @@ do local t=P.freshTime if not ifpre then - P:freshBlock('move') + P:freshMoveBlock() end if fdy>0 and P.freshTime==t and P.curY~=P.imgY then P.freshTime=P.freshTime-1 @@ -685,7 +690,7 @@ do local sfx if ifpre then sfx='prerotate' - elseif P:ifoverlap(icb,x,y+1)and P:ifoverlap(icb,x-1,y)and P:ifoverlap(icb,x+1,y)then + elseif y==P.ghoY and P:ifoverlap(icb,x,y+1) and P:ifoverlap(icb,x-1,y) and P:ifoverlap(icb,x+1,y) then sfx='rotatekick' P:_rotateField(d) else @@ -700,7 +705,7 @@ do end end - --Try release left/right, then softdrop, failed to rotate otherwise + -- Try release left/right, then softdrop, failed to rotate otherwise if dx~=0 then dx=0 elseif dy~=0 then @@ -715,37 +720,37 @@ end local ARS_Z do - local lrOnly={'+0+0','+1+0','-1+0'} - local upOnly={'+0+0','+0+1','+0+2'} - local lrSet={ - [01]=lrOnly,[10]=lrOnly,[03]=lrOnly,[30]=lrOnly, - [12]=lrOnly,[21]=lrOnly,[32]=lrOnly,[23]=lrOnly, - [02]=lrOnly,[20]=lrOnly,[13]=lrOnly,[31]=lrOnly, + local list3={'+0+0','+1+0','-1+0','+0+1'} + local list4={'+0+0','+1+0','-1+0','+0+1','+0+2','+2+0','-2+0'} + local set3={ + [01]=list3,[10]=list3,[03]=list3,[30]=list3, + [12]=list3,[21]=list3,[32]=list3,[23]=list3, + [02]=list3,[20]=list3,[13]=list3,[31]=list3, } - local upSet={ - [01]=upOnly,[10]=upOnly,[03]=upOnly,[30]=upOnly, - [12]=upOnly,[21]=upOnly,[32]=upOnly,[23]=upOnly, - [02]=upOnly,[20]=upOnly,[13]=upOnly,[31]=upOnly, + local set4={ + [01]=list4,[10]=list4,[03]=list4,[30]=list4, + [12]=list4,[21]=list4,[32]=list4,[23]=list4, + [02]=list4,[20]=list4,[13]=list4,[31]=list4, } local centerPos=TABLE.copy(defaultCenterPos) - centerPos[1]={[0]={1,1},{1,0},{1,1},{1,0}}--Z - centerPos[2]={[0]={1,1},{1,1},{1,1},{1,1}}--S - centerPos[3]={[0]={1,1},{1,0},{1,1},{1,1}}--L - centerPos[4]={[0]={1,1},{1,0},{1,1},{1,1}}--J - centerPos[5]={[0]={1,1},{1,0},{1,1},{1,1}}--T - centerPos[7]={[0]={0,2},{2,0},{0,2},{2,0}}--I - centerPos[10]={[0]={1,1},{1,0},{1,1},{1,0}}--P - centerPos[11]={[0]={1,1},{1,1},{1,1},{1,1}}--Q - centerPos[15]={[0]={1,1},{1,0},{1,1},{1,1}}--U - centerPos[16]={[0]={1,1},{1,1},{1,1},{1,1}}--V - centerPos[19]={[0]={1.5,1.5},{1.5,0.5},{1.5,1.5},{1.5,0.5}}--J5 - centerPos[20]={[0]={1.5,1.5},{1.5,0.5},{1.5,1.5},{1.5,0.5}}--L5 - centerPos[21]={[0]={1.5,1.5},{1.5,0.5},{1.5,1.5},{1.5,0.5}}--R - centerPos[22]={[0]={1.5,1.5},{1.5,0.5},{1.5,1.5},{1.5,0.5}}--Y - centerPos[23]={[0]={1.5,1.5},{1.5,0.5},{1.5,1.5},{1.5,0.5}}--N - centerPos[24]={[0]={1.5,1.5},{1.5,0.5},{1.5,1.5},{1.5,0.5}}--H - centerPos[26]={[0]={0,1},{0,0},{0,1},{0,0}}--I3 - centerPos[28]={[0]={0,1},{0,0},{0,1},{0,0}}--I2 + centerPos[1]={[0]={1,1},{1,0},{1,1},{1,0}}-- Z + centerPos[2]={[0]={1,1},{1,1},{1,1},{1,1}}-- S + centerPos[3]={[0]={1,1},{1,0},{1,1},{1,1}}-- L + centerPos[4]={[0]={1,1},{1,0},{1,1},{1,1}}-- J + centerPos[5]={[0]={1,1},{1,0},{1,1},{1,1}}-- T + centerPos[7]={[0]={0,2},{2,0},{0,2},{2,0}}-- I + centerPos[10]={[0]={1,1},{1,0},{1,1},{1,0}}-- P + centerPos[11]={[0]={1,1},{1,1},{1,1},{1,1}}-- Q + centerPos[15]={[0]={1,1},{1,0},{1,1},{1,1}}-- U + centerPos[16]={[0]={1,1},{1,1},{1,1},{1,1}}-- V + centerPos[19]={[0]={1.5,1.5},{1.5,0.5},{1.5,1.5},{1.5,0.5}}-- J5 + centerPos[20]={[0]={1.5,1.5},{1.5,0.5},{1.5,1.5},{1.5,0.5}}-- L5 + centerPos[21]={[0]={1.5,1.5},{1.5,0.5},{1.5,1.5},{1.5,0.5}}-- R + centerPos[22]={[0]={1.5,1.5},{1.5,0.5},{1.5,1.5},{1.5,0.5}}-- Y + centerPos[23]={[0]={1.5,1.5},{1.5,0.5},{1.5,1.5},{1.5,0.5}}-- N + centerPos[24]={[0]={1.5,1.5},{1.5,0.5},{1.5,1.5},{1.5,0.5}}-- H + centerPos[26]={[0]={0,1},{0,0},{0,1},{0,0}}-- I3 + centerPos[28]={[0]={0,1},{0,0},{0,1},{0,0}}-- I2 ARS_Z={ centerTex=GC.DO{10,10, @@ -756,33 +761,32 @@ do {'line',2,8,5,2,8,8}, }, centerPos=centerPos, - kickTable=TABLE.new(lrSet,29), + kickTable=TABLE.new(set3,29), } - ARS_Z.kickTable[7]=upSet - ARS_Z.kickTable[25]=upSet + ARS_Z.kickTable[7],ARS_Z.kickTable[25]=set4,set4-- I,I5 end local DRS_weak do local centerPos=TABLE.copy(defaultCenterPos) - centerPos[1]={[0]={1,1},{1,0},{1,1},{1,1}}--Z - centerPos[2]={[0]={1,1},{1,0},{1,1},{1,1}}--S - centerPos[3]={[0]={1,1},{1,0},{1,1},{1,1}}--L - centerPos[4]={[0]={1,1},{1,0},{1,1},{1,1}}--J - centerPos[5]={[0]={1,1},{1,0},{1,1},{1,1}}--T - centerPos[7]={[0]={.5,1.5},{1.5,-.5},{.5,1.5},{1.5,.5}}--I - centerPos[10]={[0]={1,1},{1,0},{1,1},{1,0}}--P - centerPos[11]={[0]={1,1},{1,1},{1,1},{1,1}}--Q - centerPos[15]={[0]={1,1},{1,0},{1,1},{1,1}}--U - centerPos[16]={[0]={1,1},{1,1},{1,1},{1,1}}--V - centerPos[19]={[0]={1.5,1.5},{1.5,0.5},{1.5,1.5},{1.5,0.5}}--J5 - centerPos[20]={[0]={1.5,1.5},{1.5,0.5},{1.5,1.5},{1.5,0.5}}--L5 - centerPos[21]={[0]={1.5,1.5},{1.5,0.5},{1.5,1.5},{1.5,0.5}}--R - centerPos[22]={[0]={1.5,1.5},{1.5,0.5},{1.5,1.5},{1.5,0.5}}--Y - centerPos[23]={[0]={1.5,1.5},{1.5,0.5},{1.5,1.5},{1.5,0.5}}--N - centerPos[24]={[0]={1.5,1.5},{1.5,0.5},{1.5,1.5},{1.5,0.5}}--H - centerPos[26]={[0]={0,1},{0,0},{0,1},{0,0}}--I3 - centerPos[28]={[0]={0,1},{0,0},{0,1},{0,0}}--I2 + centerPos[1]={[0]={1,1},{1,0},{1,1},{1,1}}-- Z + centerPos[2]={[0]={1,1},{1,0},{1,1},{1,1}}-- S + centerPos[3]={[0]={1,1},{1,0},{1,1},{1,1}}-- L + centerPos[4]={[0]={1,1},{1,0},{1,1},{1,1}}-- J + centerPos[5]={[0]={1,1},{1,0},{1,1},{1,1}}-- T + centerPos[7]={[0]={.5,1.5},{1.5,-.5},{.5,1.5},{1.5,.5}}-- I + centerPos[10]={[0]={1,1},{1,0},{1,1},{1,0}}-- P + centerPos[11]={[0]={1,1},{1,1},{1,1},{1,1}}-- Q + centerPos[15]={[0]={1,1},{1,0},{1,1},{1,1}}-- U + centerPos[16]={[0]={1,1},{1,1},{1,1},{1,1}}-- V + centerPos[19]={[0]={1.5,1.5},{1.5,0.5},{1.5,1.5},{1.5,0.5}}-- J5 + centerPos[20]={[0]={1.5,1.5},{1.5,0.5},{1.5,1.5},{1.5,0.5}}-- L5 + centerPos[21]={[0]={1.5,1.5},{1.5,0.5},{1.5,1.5},{1.5,0.5}}-- R + centerPos[22]={[0]={1.5,1.5},{1.5,0.5},{1.5,1.5},{1.5,0.5}}-- Y + centerPos[23]={[0]={1.5,1.5},{1.5,0.5},{1.5,1.5},{1.5,0.5}}-- N + centerPos[24]={[0]={1.5,1.5},{1.5,0.5},{1.5,1.5},{1.5,0.5}}-- H + centerPos[26]={[0]={0,1},{0,0},{0,1},{0,0}}-- I3 + centerPos[28]={[0]={0,1},{0,0},{0,1},{0,0}}-- I2 local L={'+0+0','-1+0','+1+0','+0-1','-1-1','+1-1'} local R={'+0+0','+1+0','-1+0','+0-1','+1-1','-1-1'} @@ -802,36 +806,47 @@ do }, centerPos=centerPos, kickTable={ - Z,S,--Z,S - Z,S,--J,L - Z,--T - noKickSet,--O - Z,--I + Z,S,-- Z,S + Z,S,-- J,L + Z,-- T + noKickSet,-- O + Z,-- I - Z,S,--Z5,S5 - Z,S,--P,Q - Z,S,--F,E - Z,Z,Z,Z,--T5,U,V,W - noKickSet,--X - Z,S,--J5,L5 - Z,S,--R,Y - Z,S,--N,H - Z,--I5 + Z,S,-- Z5,S5 + Z,S,-- P,Q + Z,S,-- F,E + Z,Z,Z,Z,-- T5,U,V,W + noKickSet,-- X + Z,S,-- J5,L5 + Z,S,-- R,Y + Z,S,-- N,H + Z,-- I5 - Z,Z,--I3,C - Z,Z,--I2,O1 + Z,Z,-- I3,C + Z,Z,-- I2,O1 } } end local ASC do - local L={'+0+0','+1+0','+0-1','+1-1','+0-2','+1-2','+2+0','+2-1','+2-2','-1+0','-1-1','+0+1','+1+1','+2+1','-1-2','-2+0','+0+2','+1+2','+2+2','-2-1','-2-2'} + local L={'+0+0','+1+0','+0-1','+1-1','+0-2','+1-2','+2+0','+2-1','+2-2','-1+0','-1-1','+0+1','+1+1','+2+1','-1-2','-2+0','+0+2','+1+2','+2+2','-2-1','-2-2','-1+1'} local R=_flipList(L) local F={'+0+0'} local centerPos=TABLE.copy(defaultCenterPos) - centerPos[6]={[0]={0,0},{1,0},{1,1},{0,1}} - centerPos[7]={[0]={0,1},{2,0},{0,2},{1,0}} + centerPos[6]={[0]={0,0},{1,0},{1,1},{0,1}}-- O + centerPos[7]={[0]={0,1},{2,0},{0,2},{1,0}}-- I + centerPos[14]={[0]={0,1},{1,0},{2,1},{1,2}}-- T5 + centerPos[16]={[0]={1,1},{1,1},{1,1},{1,1}}-- V + centerPos[19]={[0]={0,1},{2,0},{1,2},{1,1}}-- J5 + centerPos[20]={[0]={0,2},{1,0},{1,1},{2,1}}-- L5 + centerPos[21]={[0]={0,2},{1,0},{1,1},{2,1}}-- R + centerPos[22]={[0]={0,1},{2,0},{1,2},{1,1}}-- Y + centerPos[23]={[0]={0,1},{2,0},{1,2},{1,1}}-- N + centerPos[24]={[0]={0,2},{1,0},{1,1},{2,1}}-- H + centerPos[27]={[0]={0,1},{0,0},{1,0},{1,1}}-- C + centerPos[28]={[0]={0,1},{0,0},{0,0},{1,0}}-- I2 + ASC={ centerTex=GC.DO{10,10, {'setLW',2}, @@ -854,10 +869,10 @@ end local ASC_plus do - local L={'+0+0','+1+0','+0-1','+1-1','+0-2','+1-2','+2+0','+2-1','+2-2','-1+0','-1-1','+0+1','+1+1','+2+1','-1-2','-2+0','+0+2','+1+2','+2+2','-2-1','-2-2'} + local L={'+0+0','+1+0','+0-1','+1-1','+0-2','+1-2','+2+0','+2-1','+2-2','-1+0','-1-1','+0+1','+1+1','+2+1','-1-2','-2+0','+0+2','+1+2','+2+2','-2-1','-2-2','-1+1'} local R=_flipList(L) local F={'+0+0','-1+0','+1+0','+0-1','-1-1','+1-1','+0-2','-1-2','+1-2','-2+0','+2+0','-2-1','+2-1','-2+1','+2+1','+0+2','-1+2','+1+2'} - local centerPos=TABLE.copy(defaultCenterPos) + local centerPos=TABLE.copy(ASC.centerPos) centerPos[6]={[0]={0,0},{1,0},{1,1},{0,1}} centerPos[7]={[0]={0,1},{2,0},{0,2},{1,0}} ASC_plus={ @@ -916,28 +931,62 @@ do {'fRect',3,3,4,4}, }, kickTable={ - Z,S,--Z,S - Z,S,--J,L - Z,--T - noKickSet,--O - Z,--I + Z,S,-- Z,S + Z,S,-- J,L + Z,-- T + noKickSet,-- O + Z,-- I - Z,S,--Z5,S5 - Z,S,--P,Q - Z,S,--F,E - Z,Z,Z,Z,--T5,U,V,W - noKickSet,--X - Z,S,--J5,L5 - Z,S,--R,Y - Z,S,--N,H - Z,--I5 + Z,S,-- Z5,S5 + Z,S,-- P,Q + Z,S,-- F,E + Z,Z,Z,Z,-- T5,U,V,W + noKickSet,-- X + Z,S,-- J5,L5 + Z,S,-- R,Y + Z,S,-- N,H + Z,-- I5 - Z,Z,--I3,C - Z,Z,--I2,O1 + Z,Z,-- I3,C + Z,Z,-- I2,O1 } } end +local N64 +do + local R={'+0+0','+0-1','+1+0','-1+0','+0+1'} + local L={'+0+0','+0-1','-1+0','+1+0','+0+1'} + N64={ + centerTex=GC.DO{10,10, + {'setLW',2}, + {'line',2,9,2,1,8,9,8,1}, + }, + kickTable=TABLE.new({ + [01]=R,[10]=L,[12]=R,[21]=L, + [23]=R,[32]=L,[30]=R,[03]=L, + },29) + } +end + +local N64_plus +do + local R={'+0+0','+0-1','+1+0','-1+0','+0+1'} + local L={'+0+0','+0-1','-1+0','+1+0','+0+1'} + local F={'+0+0','+0-1','+0+1'} + N64_plus={ + centerTex=GC.DO{10,10, + {'setLW',2}, + {'line',4,9,2,9,2,1,8,9,8,1,6,1}, + }, + kickTable=TABLE.new({ + [01]=R,[10]=L,[12]=R,[21]=L, + [23]=R,[32]=L,[30]=R,[03]=L, + [02]=F,[20]=F,[13]=F,[31]=F, + },29) + } +end + local Classic do local centerPos=TABLE.copy(defaultCenterPos) centerPos[1]={[0]={1,1},{1,0},{1,1},{1,0}} @@ -978,7 +1027,7 @@ local None={ {'setLW',2}, {'line',2,2,6,6}, }, - kickTable=TABLE.new(noKickSet_180,29) + kickTable=TABLE.new(noKickSet,29) } local None_plus={ @@ -987,7 +1036,7 @@ local None_plus={ {'line',1,1,7,7}, {'fRect',2,2,4,4}, }, - kickTable=TABLE.new(noKickSet,29) + kickTable=TABLE.new(noKickSet_180,29) } local RSlist={ @@ -1002,22 +1051,25 @@ local RSlist={ ASC_plus=ASC_plus, C2=C2, C2_sym=C2_sym, + N64=N64, + N64_plus=N64_plus, Classic=Classic, Classic_plus=Classic_plus, None=None, None_plus=None_plus, } -for _,rs in next,RSlist do - if not rs.centerDisp then rs.centerDisp=TABLE.new(true,29)end +for name,rs in next,RSlist do + rs.name=name + if not rs.centerDisp then rs.centerDisp=TABLE.new(true,29) end if not rs.centerPos then rs.centerPos=defaultCenterPos end if not rs.centerTex then rs.centerTex=defaultCenterTex end - --Make all string vec to the same table vec + -- Make all string vec to the same table vec for _,set in next,rs.kickTable do - if type(set)=='table'then + if type(set)=='table' then for _,list in next,set do - if type(list[1])=='string'then + if type(list[1])=='string' then _strToVec(list) end end @@ -1025,4 +1077,4 @@ for _,rs in next,RSlist do end end -return RSlist +return RSlist \ No newline at end of file diff --git a/parts/backgrounds/aura.lua b/parts/backgrounds/aura.lua index a76030deb..eaea505a6 100644 --- a/parts/backgrounds/aura.lua +++ b/parts/backgrounds/aura.lua @@ -1,21 +1,19 @@ ---Cool liquid background -local gc=love.graphics +-- Cool liquid background local back={} local shader=SHADER.aura local t function back.init() - t=math.random()*260 - BG.resize(SCR.w,SCR.h) + t=math.random()*2600 end function back.update(dt) - t=(t+dt)%2600 + t=(t+dt)%6200 end function back.draw() - gc.clear(.08,.08,.084) + GC.clear(.08,.08,.084) shader:send('phase',t) - gc.setShader(shader) - gc.rectangle('fill',0,0,SCR.w,SCR.h) - gc.setShader() + GC.setShader(shader) + GC.rectangle('fill',0,0,SCR.w,SCR.h) + GC.setShader() end return back diff --git a/parts/backgrounds/bg1.lua b/parts/backgrounds/bg1.lua index 6e1a7f5eb..a5bb9f42c 100644 --- a/parts/backgrounds/bg1.lua +++ b/parts/backgrounds/bg1.lua @@ -1,20 +1,19 @@ ---Horizonal red-blue gradient -local gc=love.graphics +-- Horizonal red-blue gradient local back={} local shader=SHADER.grad1 local t function back.init() - t=math.random()*260 + t=math.random()*2600 end function back.update(dt) - t=(t+dt)%2600 + t=(t+dt)%6200 end function back.draw() - gc.clear(.08,.08,.084) + GC.clear(.08,.08,.084) shader:send('phase',t) - gc.setShader(shader) - gc.rectangle('fill',0,0,SCR.w,SCR.h) - gc.setShader() + GC.setShader(shader) + GC.rectangle('fill',0,0,SCR.w,SCR.h) + GC.setShader() end return back diff --git a/parts/backgrounds/bg2.lua b/parts/backgrounds/bg2.lua index e6178e589..b7af6541c 100644 --- a/parts/backgrounds/bg2.lua +++ b/parts/backgrounds/bg2.lua @@ -1,21 +1,19 @@ ---Vertical red-green gradient -local gc=love.graphics +-- Vertical red-green gradient local back={} local shader=SHADER.grad2 local t function back.init() - t=math.random()*260 - BG.resize(nil,SCR.h) + t=math.random()*2600 end function back.update(dt) - t=(t+dt)%2600 + t=(t+dt)%6200 end function back.draw() - gc.clear(.08,.08,.084) + GC.clear(.08,.08,.084) shader:send('phase',t) - gc.setShader(shader) - gc.rectangle('fill',0,0,SCR.w,SCR.h) - gc.setShader() + GC.setShader(shader) + GC.rectangle('fill',0,0,SCR.w,SCR.h) + GC.setShader() end return back diff --git a/parts/backgrounds/blockfall.lua b/parts/backgrounds/blockfall.lua index 4ecb0a74f..bce4c32b1 100644 --- a/parts/backgrounds/blockfall.lua +++ b/parts/backgrounds/blockfall.lua @@ -1,4 +1,4 @@ ---Large falling tetrominoes +-- Large falling tetrominoes local gc=love.graphics local gc_clear=gc.clear local gc_setColor,gc_draw=gc.setColor,gc.draw @@ -43,8 +43,8 @@ function back.draw() local M=mino[i] local b=M.block for y=1,#b do - for x=1,#b[1]do - if b[y][x]then + for x=1,#b[1] do + if b[y][x] then gc_draw(M.texture,M.x+(x-1)*30*M.k,M.y-y*30*M.k,nil,M.k) end end diff --git a/parts/backgrounds/blockhole.lua b/parts/backgrounds/blockhole.lua index 8f14f2ac2..7cdc00aa8 100644 --- a/parts/backgrounds/blockhole.lua +++ b/parts/backgrounds/blockhole.lua @@ -1,4 +1,4 @@ ---blockhole +-- blockhole local gc=love.graphics local gc_clear,gc_replaceTransform=gc.clear,gc.replaceTransform local gc_setColor,gc_setLineWidth=gc.setColor,gc.setLineWidth @@ -19,9 +19,9 @@ function back.update() t=t-1 if t==0 then local S={ - ang=6.2832*rnd(), + ang=MATH.tau*rnd(), d=SCR.rad*1.05/2, - rotate=6.2832*rnd(), + rotate=MATH.tau*rnd(), va=.05-rnd()*.1, size=SCR.rad*(2+rnd()*3)/100, texture=SKIN.lib[SETTING.skinSet][rnd(16)], @@ -44,14 +44,14 @@ function back.draw() gc_clear(.1,.1,.1) gc_replaceTransform(SCR.xOy_m) - --Squares + -- Squares gc_setColor(1,1,1,.2) for i=1,#squares do local S=squares[i] gc_draw(S.texture,S.d*cos(S.ang),S.d*sin(S.ang),S.rotate,S.size*.026,nil,15,15) end - --blockhole + -- blockhole gc_setColor(.07,.07,.07) gc_circle('fill',0,0,157) gc_setLineWidth(6) diff --git a/parts/backgrounds/blockrain.lua b/parts/backgrounds/blockrain.lua index c1a6b7759..f7d6fe4b4 100644 --- a/parts/backgrounds/blockrain.lua +++ b/parts/backgrounds/blockrain.lua @@ -1,4 +1,4 @@ ---Block rain +-- Block rain local gc=love.graphics local rnd=math.random local ins,rem=table.insert,table.remove @@ -21,7 +21,7 @@ function back.update() x=SCR.w*rnd(), y=SCR.h*-.05, k=SCR.rad/200, - ang=rnd()*6.2832, + ang=rnd()*MATH.tau, vy=.5+rnd()*.4, vx=rnd()*.4-.2, va=rnd()*.04-.02, diff --git a/parts/backgrounds/blockspace.lua b/parts/backgrounds/blockspace.lua index 9011a98ce..ec36ed997 100644 --- a/parts/backgrounds/blockspace.lua +++ b/parts/backgrounds/blockspace.lua @@ -1,4 +1,4 @@ ---Space but tetrominoes +-- Space but tetrominoes local gc=love.graphics local gc_clear,gc_translate=gc.clear,gc.translate local gc_setColor,gc_draw=gc.setColor,gc.draw @@ -21,8 +21,8 @@ function back.update() ins(mino,{ block=TEXTURE.miniBlock[r], color=BLOCK_COLORS[SETTING.skin[r]], - ang=6.2832*rnd(), - rotate=6.2832*rnd(), + ang=MATH.tau*rnd(), + rotate=MATH.tau*rnd(), vr=.05-rnd()*.1, d=0, v=.5+rnd(), diff --git a/parts/backgrounds/cubes.lua b/parts/backgrounds/cubes.lua index 84442a1fb..bbf9724c9 100644 --- a/parts/backgrounds/cubes.lua +++ b/parts/backgrounds/cubes.lua @@ -1,4 +1,4 @@ ---Flying cubes +-- Flying cubes local gc=love.graphics local gc_clear=gc.clear local gc_setColor,gc_setLineWidth=gc.setColor,gc.setLineWidth diff --git a/parts/backgrounds/custom.lua b/parts/backgrounds/custom.lua index a881b8e9b..06afbe503 100644 --- a/parts/backgrounds/custom.lua +++ b/parts/backgrounds/custom.lua @@ -1,4 +1,4 @@ ---Custom background +-- Custom background local gc_clear,gc_setColor=love.graphics.clear,love.graphics.setColor local back={} diff --git a/parts/backgrounds/fan.lua b/parts/backgrounds/fan.lua index aaa46fc32..2938fe1a5 100644 --- a/parts/backgrounds/fan.lua +++ b/parts/backgrounds/fan.lua @@ -1,4 +1,4 @@ ---Yuyuko's fan +-- Yuyuko's fan local gc=love.graphics local gc_clear,gc_origin,gc_replaceTransform=gc.clear,gc.origin,gc.replaceTransform local gc_translate=gc.translate @@ -61,7 +61,7 @@ function back.draw() gc_setLineWidth(6) gc_setColor(.8,.9,1,.3) - for i=1,#SVG_TITLE_FAN do gc_polygon('line',SVG_TITLE_FAN[i])end + for i=1,#SVG_TITLE_FAN do gc_polygon('line',SVG_TITLE_FAN[i]) end gc_setLineWidth(2) gc_setColor(1,.5,.7,.3) diff --git a/parts/backgrounds/firework.lua b/parts/backgrounds/firework.lua index 33c5ae709..93493e97c 100644 --- a/parts/backgrounds/firework.lua +++ b/parts/backgrounds/firework.lua @@ -1,4 +1,4 @@ ---Firework +-- Firework local gc=love.graphics local gc_clear=gc.clear local gc_setColor,gc_setLineWidth=gc.setColor,gc.setLineWidth @@ -26,9 +26,10 @@ function back.update(dt) t=0, v=.5+rnd(), color=COLOR.random_dark(), - big=rnd()<.1, + big=rnd()<.12, }) - t=rnd(26,62) + t=rnd(42,126) + SFX.play('rotate',.5,0,-16) end for i=#firework,1,-1 do local F=firework[i] @@ -37,7 +38,7 @@ function back.update(dt) local x,y,color=F.x,F.y,F.color if F.big then SFX.play('fall',.5) - for _=1,rnd(62,126)do + for _=1,rnd(62,126) do ins(particle,{ x=x,y=y, color=color, @@ -48,7 +49,7 @@ function back.update(dt) end else SFX.play('clear_1',.4) - for _=1,rnd(16,26)do + for _=1,rnd(16,26) do ins(particle,{ x=x,y=y, color=color, diff --git a/parts/backgrounds/fixColor.lua b/parts/backgrounds/fixColor.lua index 5ae8b32d8..7bc8b9aaa 100644 --- a/parts/backgrounds/fixColor.lua +++ b/parts/backgrounds/fixColor.lua @@ -1,9 +1,8 @@ ---Customizable grey background -local gc=love.graphics +-- Customizable grey background local back={} local r,g,b=.26,.26,.26 function back.draw() - gc.clear(r,g,b) + GC.clear(r,g,b) end function back.event(_r,_g,_b) r,g,b=_r,_g,_b diff --git a/parts/backgrounds/flink.lua b/parts/backgrounds/flink.lua index 39b74e663..73112c254 100644 --- a/parts/backgrounds/flink.lua +++ b/parts/backgrounds/flink.lua @@ -1,5 +1,4 @@ ---Flash after random time -local gc=love.graphics +-- Flash after random time local back={} local t @@ -11,8 +10,8 @@ function back.update(dt) end function back.draw() local t1=.13-t%3%1.9 - if t1<.2 then gc.clear(t1,t1,t1) - else gc.clear(0,0,0) + if t1<.2 then GC.clear(t1,t1,t1) + else GC.clear(0,0,0) end end return back diff --git a/parts/backgrounds/galaxy.lua b/parts/backgrounds/galaxy.lua new file mode 100644 index 000000000..bab729197 --- /dev/null +++ b/parts/backgrounds/galaxy.lua @@ -0,0 +1,41 @@ +--Space with stars +local gc=love.graphics +local circle,setColor,hsv=gc.circle,gc.setColor,COLOR.hsv +local sin,cos=math.sin,math.cos +local back={} + +local sDist,sRev={},{} -- star data in SoA [distance from center, revolution progress, color] + +function back.init() + if sDist[1] then return end + local max + for i=0,20 do + max=16*(i+1) + for j=1,max do + sDist[#sDist+1]=i+math.random() + sRev[#sRev+1]=MATH.tau*j/max+MATH.tau*math.random()/max + end + end +end +function back.update(dt) + for i=1,#sDist do + sRev[i]=(sRev[i]+dt/(sDist[i]+1))%MATH.tau + end +end +function back.draw() + gc.clear() + gc.translate(SCR.cx,SCR.cy) + gc.scale(SCR.k) + gc.rotate(1) + for i=1,#sDist do + local d,r=sDist[i],sRev[i] + if d<5 then + setColor(hsv(.088,(d-2)/7,1,.2)) + else + setColor(hsv(.572,d/70+.1,(22-d)/12,.2)) + end + circle('fill',8*d*cos(r),24*d*sin(r),5) + end +end + +return back diff --git a/parts/backgrounds/glow.lua b/parts/backgrounds/glow.lua index 792a5a9b7..42e4a8ad5 100644 --- a/parts/backgrounds/glow.lua +++ b/parts/backgrounds/glow.lua @@ -1,5 +1,4 @@ ---Light-dark -local gc=love.graphics +-- Light-dark local sin=math.sin local back={} @@ -12,6 +11,6 @@ function back.update(dt) end function back.draw() local t1=(sin(t*.5)+sin(t*.7)+sin(t*.9+1)+sin(t*1.5)+sin(t*2+10))*.08 - gc.clear(t1,t1,t1) + GC.clear(t1,t1,t1) end return back diff --git a/parts/backgrounds/lanterns.lua b/parts/backgrounds/lanterns.lua index 63d606812..cef3ccd6e 100644 --- a/parts/backgrounds/lanterns.lua +++ b/parts/backgrounds/lanterns.lua @@ -1,6 +1,5 @@ ---A lantern background which is full of festive atmosphere. Lantern image by ScF -local gc=love.graphics -local int,rnd=math.floor,math.random +-- A lantern background which is full of festive atmosphere. Lantern image by ScF +local floor,rnd=math.floor,math.random local ins,rem=table.insert,table.remove local mDraw=mDraw local back={} @@ -37,12 +36,12 @@ function back.update(dt) end end function back.draw() - gc.clear(.08,.08,.084) - gc.setColor(1,1,1,.2) + GC.clear(.08,.08,.084) + GC.setColor(1,1,1,.2) local img=IMG.lanterns for i=1,#lanterns do local L=lanterns[i] - mDraw(img[int(L.phase*6)+1],L.x,L.y,nil,L.size) + mDraw(img[floor(L.phase*6)+1],L.x,L.y,nil,L.size) end end return back diff --git a/parts/backgrounds/league.lua b/parts/backgrounds/league.lua index a940207ab..265082338 100644 --- a/parts/backgrounds/league.lua +++ b/parts/backgrounds/league.lua @@ -1,5 +1,4 @@ ---Space with stars -local gc=love.graphics +-- Space with stars local back={} local upCover do @@ -29,8 +28,8 @@ end function back.update() end function back.draw() - gc.clear(.08,.08,.084) - gc.draw(upCover,0,0,0,W,H*.3/64) - gc.draw(downCover,0,H*.7,0,W,H*.3/64) + GC.clear(.08,.08,.084) + GC.draw(upCover,0,0,0,W,H*.3/64) + GC.draw(downCover,0,H*.7,0,W,H*.3/64) end return back diff --git a/parts/backgrounds/lightning.lua b/parts/backgrounds/lightning.lua index 7bac2b457..0ae685844 100644 --- a/parts/backgrounds/lightning.lua +++ b/parts/backgrounds/lightning.lua @@ -1,5 +1,4 @@ ---Lightning -local gc=love.graphics +-- Lightning local back={} local t @@ -11,8 +10,8 @@ function back.update(dt) end function back.draw() local t1=2.5-t%20%6%2.5 - if t1<.3 then gc.clear(t1,t1,t1) - else gc.clear(0,0,0) + if t1<.3 then GC.clear(t1,t1,t1) + else GC.clear(0,0,0) end end return back diff --git a/parts/backgrounds/lightning2.lua b/parts/backgrounds/lightning2.lua index a0716cfd3..75dbc8646 100644 --- a/parts/backgrounds/lightning2.lua +++ b/parts/backgrounds/lightning2.lua @@ -1,6 +1,5 @@ ---Fast lightning + spining tetromino -local gc=love.graphics -local int,rnd=math.floor,math.random +-- Fast lightning + spining tetromino +local floor,rnd=math.floor,math.random local back={} local t @@ -13,13 +12,13 @@ function back.update(dt) t=t+dt end function back.draw() - local R=7-int(t*.5%7) + local R=7-floor(t*.5%7) local T=1.2-t%15%6%1.8 - if T<.26 then gc.clear(T,T,T) - else gc.clear(0,0,0) + if T<.26 then GC.clear(T,T,T) + else GC.clear(0,0,0) end local _=colorLib[SETTING.skin[R]] - gc.setColor(_[1],_[2],_[3],.12) - gc.draw(TEXTURE.miniBlock[R],SCR.cx,SCR.cy,t%3.1416*6,200*SCR.k,nil,2*DSCP[R][0][2]+1,2*(#blocks[R][0]-DSCP[R][0][1])-1) + GC.setColor(_[1],_[2],_[3],.12) + GC.draw(TEXTURE.miniBlock[R],SCR.cx,SCR.cy,t%3.1416*6,200*SCR.k,nil,2*DSCP[R][0][2]+1,2*(#blocks[R][0]-DSCP[R][0][1])-1) end return back diff --git a/parts/backgrounds/matrix.lua b/parts/backgrounds/matrix.lua index d487d801c..e1d413d47 100644 --- a/parts/backgrounds/matrix.lua +++ b/parts/backgrounds/matrix.lua @@ -1,4 +1,4 @@ ---Black-White grid +-- Black-White grid local gc=love.graphics local gc_clear,gc_scale=gc.clear,gc.scale local gc_setColor=gc.setColor @@ -9,7 +9,7 @@ local ceil=math.ceil local back={} local t -local matrixT={}for i=1,50 do matrixT[i]={}for j=1,50 do matrixT[i][j]=love.math.noise(i,j)+2 end end +local matrixT={} for i=1,50 do matrixT[i]={} for j=1,50 do matrixT[i][j]=love.math.noise(i,j)+2 end end function back.init() t=math.random()*2600 end @@ -21,7 +21,7 @@ function back.draw() local k=SCR.k gc_scale(k) local Y=ceil(SCR.h/80/k) - for x=1,ceil(SCR.w/80/k)do + for x=1,ceil(SCR.w/80/k) do for y=1,Y do gc_setColor(1,1,1,sin(x+matrixT[x][y]*t)*.04+.04) gc_rectangle('fill',80*x,80*y,-80,-80) diff --git a/parts/backgrounds/quarks.lua b/parts/backgrounds/quarks.lua new file mode 100644 index 000000000..2159d6779 --- /dev/null +++ b/parts/backgrounds/quarks.lua @@ -0,0 +1,145 @@ +local gc=love.graphics +local hsv=COLOR.hsv +local circle,push,pop,rot,translate,setColor=gc.circle,gc.push,gc.pop,gc.rotate,gc.translate,gc.setColor +local rnd,sin,cos,log=math.random,math.sin,math.cos,math.log +local back={} + +local qX,qY,qdX,qdY={},{},{},{} -- quark data in SoA [size, X, Y, dx, dy, color] + +local ptcclr={{1,0,0,.5},{0,1,0,.5},{0,0,1,.5}} +local apcclr={{0,1,1,.5},{1,0,1,.5},{1,1,0,.5}} + +local blasts={} -- data about annihilation blasts from particles and antiparticles colliding +local ptc={} -- particle-antiparticle data (antiparticle is a mirror of particle) +local nextpair + +local W,H,size +local quarkCount=400 + +local function spawnQuarkRandom(i) + qX[i]=rnd(W)-10 -- X + qY[i]=rnd(H)-10 -- Y + local theta=rnd()*MATH.tau + qdX[i]=cos(theta)*300 -- dx + qdY[i]=sin(theta)*300 -- dy +end +local function spawnQuarkEdge(i) + local side=rnd(4) + if side==1 then -- Up edge of screen + qX[i]=rnd(SCR.x-10,SCR.ex+10) + qY[i]=SCR.y-10 + elseif side==2 then -- Right edge of screen + qX[i]=SCR.ex+10 + qY[i]=rnd(SCR.y-10,SCR.ey+10) + elseif side==3 then -- Down edge of screen + qX[i]=rnd(SCR.x-10,SCR.ex+10) + qY[i]=SCR.ey+10 + elseif side==4 then -- Left edge of screen + qX[i]=SCR.x-10 + qY[i]=rnd(SCR.y-10,SCR.ey+10) + end + local theta=rnd()*MATH.tau + qdX[i]=cos(theta)*300 -- dx + qdY[i]=sin(theta)*300 -- dy +end +local function spawnParticlePair() + ptc[#ptc+1]={ + x=rnd(W)-10, + y=rnd(H)-10, + dist=0, + theta=rnd()*MATH.tau, + v=500, + c=rnd(3), + } +end +local function spawnBlast(_x,_y) + blasts[#blasts+1]={x=_x,y=_y,t=0} +end + +function back.init() + qX,qY,qdX,qdY={},{},{},{} + blasts={} + ptc={} + nextpair=0 + BG.resize(SCR.w,SCR.h) +end +function back.resize(w,h) + W,H=w+20,h+20 + for i=1,quarkCount do spawnQuarkRandom(i) end + size=2.6*SCR.k +end +function back.update(dt) + -- Move far-away quarks + for i=1,quarkCount do + qX[i]=qX[i]+qdX[i]*dt + qY[i]=qY[i]+qdY[i]*dt + if qX[i]SCR.ex+26 or qY[i]SCR.ey+26 then + spawnQuarkEdge(i) + end + end + + -- Particle pair attraction & destruction + for i=#ptc,1,-1 do + local p=ptc[i] + if p then + p.dist=p.dist+p.v*dt + p.v=p.v-p.dist^2*dt + + -- Destroy colliding particle pairs + if p.dist<=10 and p.v<=0 then + spawnBlast(p.x,p.y) + table.remove(ptc,i) + end + end + end + + -- Age blasts, delete old blasts + for i=#blasts,1,-1 do + if blasts[i] then + blasts[i].t=blasts[i].t+dt + if blasts[i].t>=1 then + table.remove(blasts,i) + end + end + end + + -- Spawn particle pairs + nextpair=nextpair-dt + if nextpair<=0 then + spawnParticlePair() + nextpair=rnd()*4 + end +end +function back.draw() + gc.clear(.08,.04,.01) + translate(-10,-10) + + -- Draw quarks in R/G/B + setColor(1,0,0,.5) for i=1, math.floor(quarkCount/3) do circle('fill',qX[i],qY[i],size) end + setColor(0,1,0,.5) for i=math.floor(quarkCount/3)+1, math.floor(quarkCount*2/3) do circle('fill',qX[i],qY[i],size) end + setColor(0,0,1,.5) for i=math.floor(quarkCount*2/3)+1,quarkCount do circle('fill',qX[i],qY[i],size) end + + for i=1,#ptc do + local p=ptc[i] + push() + translate(p.x,p.y) + rot(p.theta) + + setColor(ptcclr[p.c]) + circle('fill', p.dist,0,4*size) + setColor(apcclr[p.c]) + circle('fill',-p.dist,0,4*size) + pop() + end + for i=1,#blasts do + local t=blasts[i].t + setColor(hsv(-80*t,1-1.7*log(5*t,10),1,1-t)) + circle('fill',blasts[i].x,blasts[i].y,62*t^.3) + end +end +function back.discard() + qX,qY,qdX,qdY,qC=nil + ptc,blasts=nil + collectgarbage() +end +return back diff --git a/parts/backgrounds/rainbow.lua b/parts/backgrounds/rainbow.lua index 64ece19ed..ab82699f6 100644 --- a/parts/backgrounds/rainbow.lua +++ b/parts/backgrounds/rainbow.lua @@ -1,21 +1,19 @@ ---Colorful RGB -local gc=love.graphics +-- Colorful RGB local back={} local shader=SHADER.rgb1 local t function back.init() - t=math.random()*260 - BG.resize(SCR.w,SCR.h) + t=math.random()*2600 end function back.update(dt) - t=(t+dt)%2600 + t=(t+dt)%6200 end function back.draw() - gc.clear(.08,.08,.084) + GC.clear(.08,.08,.084) shader:send('phase',t) - gc.setShader(shader) - gc.rectangle('fill',0,0,SCR.w,SCR.h) - gc.setShader() + GC.setShader(shader) + GC.rectangle('fill',0,0,SCR.w,SCR.h) + GC.setShader() end return back diff --git a/parts/backgrounds/rainbow2.lua b/parts/backgrounds/rainbow2.lua index 57a40993e..6fae525d1 100644 --- a/parts/backgrounds/rainbow2.lua +++ b/parts/backgrounds/rainbow2.lua @@ -1,21 +1,19 @@ ---Blue RGB -local gc=love.graphics +-- Blue RGB local back={} local shader=SHADER.rgb2 local t function back.init() - t=math.random()*260 - BG.resize(SCR.w,SCR.h) + t=math.random()*2600 end function back.update(dt) - t=(t+dt)%2600 + t=(t+dt)%6200 end function back.draw() - gc.clear(.08,.08,.084) + GC.clear(.08,.08,.084) shader:send('phase',t) - gc.setShader(shader) - gc.rectangle('fill',0,0,SCR.w,SCR.h) - gc.setShader() + GC.setShader(shader) + GC.rectangle('fill',0,0,SCR.w,SCR.h) + GC.setShader() end return back diff --git a/parts/backgrounds/rgb.lua b/parts/backgrounds/rgb.lua index 1983464c0..2951e46c4 100644 --- a/parts/backgrounds/rgb.lua +++ b/parts/backgrounds/rgb.lua @@ -1,5 +1,4 @@ ---Changing pure color -local gc=love.graphics +-- Changing pure color local sin=math.sin local back={} @@ -11,7 +10,7 @@ function back.update(dt) t=t+dt end function back.draw() - gc.clear( + GC.clear( sin(t*1.2)*.06+.08, sin(t*1.5)*.06+.08, sin(t*1.9)*.06+.08 diff --git a/parts/backgrounds/snow.lua b/parts/backgrounds/snow.lua index d3a658037..d377a9262 100644 --- a/parts/backgrounds/snow.lua +++ b/parts/backgrounds/snow.lua @@ -1,4 +1,4 @@ ---Snow +-- Snow local gc=love.graphics local ellipse=gc.ellipse local rnd=math.random diff --git a/parts/backgrounds/space.lua b/parts/backgrounds/space.lua index 48be71ad8..4d2f32fe8 100644 --- a/parts/backgrounds/space.lua +++ b/parts/backgrounds/space.lua @@ -1,4 +1,4 @@ ---Space with stars +-- Space with stars local gc=love.graphics local rectangle=gc.rectangle local rnd=math.random @@ -15,16 +15,16 @@ function back.resize(w,h) local S=stars for i=1,1260,5 do local s=rnd(26,40)*.1 - S[i]=s*SCR.k --Size - S[i+1]=rnd(W)-10 --X - S[i+2]=rnd(H)-10 --Y - S[i+3]=(rnd()-.5)*.01*s--Vx - S[i+4]=(rnd()-.5)*.01*s--Vy + S[i]=s*SCR.k -- Size + S[i+1]=rnd(W)-10 -- X + S[i+2]=rnd(H)-10 -- Y + S[i+3]=(rnd()-.5)*.01*s-- Vx + S[i+4]=(rnd()-.5)*.01*s-- Vy end end function back.update(dt) local S=stars - --Star moving + -- Star moving for i=1,1260,5 do S[i+1]=(S[i+1]+S[i+3]*dt*60)%W S[i+2]=(S[i+2]+S[i+4]*dt*60)%H @@ -32,7 +32,7 @@ function back.update(dt) end function back.draw() gc.clear(.08,.08,.084) - if not stars[1]then return end + if not stars[1] then return end gc.translate(-10,-10) gc.setColor(1,1,1,.6) for i=1,1260,5 do diff --git a/parts/backgrounds/tunnel.lua b/parts/backgrounds/tunnel.lua index 103de65df..5391a25ae 100644 --- a/parts/backgrounds/tunnel.lua +++ b/parts/backgrounds/tunnel.lua @@ -1,5 +1,4 @@ ---Cool Tunnel -local gc=love.graphics +-- Cool Tunnel local rnd=math.random local ins,rem=table.insert,table.remove local back={} @@ -29,12 +28,12 @@ function back.update(dt) end end function back.draw() - gc.clear(.08,.08,.084) - gc.setColor(1,1,1,.1) + GC.clear(.08,.08,.084) + GC.setColor(1,1,1,.1) for i=1,#ring do local r=ring[i]^2/12 - gc.setLineWidth(30-15/(r+.5)) - gc.rectangle('line',W*.5-W*r/2,H*.5-H*r/2,W*r,H*r) + GC.setLineWidth(30-15/(r+.5)) + GC.rectangle('line',W*.5-W*r/2,H*.5-H*r/2,W*r,H*r) end end function back.discard() diff --git a/parts/backgrounds/welcome.lua b/parts/backgrounds/welcome.lua index 22158a851..09a95755c 100644 --- a/parts/backgrounds/welcome.lua +++ b/parts/backgrounds/welcome.lua @@ -1,5 +1,4 @@ ---Welcome to Techmino -local gc=love.graphics +-- Welcome to Techmino local sin=math.sin local back={} @@ -7,33 +6,33 @@ local t local textObj function back.init() t=math.random()*2600 - textObj=gc.newText(getFont(80),"Welcome To Techmino") + textObj=GC.newText(getFont(80),"Welcome To Techmino") end function back.update(dt) t=t+dt end function back.draw() if -t%13.55<.1283 then - gc.clear(.2+.1*sin(t),.2+.1*sin(1.26*t),.2+.1*sin(1.626*t)) + GC.clear(.2+.1*sin(t),.2+.1*sin(1.26*t),.2+.1*sin(1.626*t)) else - gc.clear(.08,.08,.084) + GC.clear(.08,.08,.084) end - gc.push('transform') - gc.replaceTransform(SCR.xOy_m) - gc.translate(0,20*sin(t*.02)) - gc.scale(1.26,1.36) + GC.push('transform') + GC.replaceTransform(SCR.xOy_m) + GC.translate(0,20*sin(t*.02)) + GC.scale(1.26,1.36) if -t%6.26<.1355 then - gc.translate(60*sin(t*.26),100*sin(t*.626)) + GC.translate(60*sin(t*.26),100*sin(t*.626)) end if -t%12.6<.1626 then - gc.rotate(t+5*sin(.26*t)+5*sin(.626*t)) + GC.rotate(t+5*sin(.26*t)+5*sin(.626*t)) end - gc.setColor(.4,.6,1,.3) + GC.setColor(.4,.6,1,.3) mDraw(textObj,4*sin(t*.7942),4*sin(t*.7355)) - gc.setColor(.5,.7,1,.4) + GC.setColor(.5,.7,1,.4) mDraw(textObj,2*sin(t*.77023),2*sin(t*.7026)) - gc.setColor(1,1,1,.5) + GC.setColor(1,1,1,.5) mDraw(textObj,3*sin(t*.7283),3*sin(t*.7626)) - gc.pop() + GC.pop() end return back diff --git a/parts/backgrounds/wing.lua b/parts/backgrounds/wing.lua index b36ffa7f1..822812fb3 100644 --- a/parts/backgrounds/wing.lua +++ b/parts/backgrounds/wing.lua @@ -1,6 +1,4 @@ ---Flandre's wing -local gc=love.graphics -local rnd=math.random +-- Flandre's wing local back={} local crystal_img,crystals local wingColor={ @@ -46,31 +44,31 @@ function back.update() end end function back.draw() - gc.clear(.06,.06,.06) - gc.setColor(.12,.10,.08) - gc.setLineJoin('bevel') - gc.setLineWidth(14*SCR.k) + GC.clear(.06,.06,.06) + GC.setColor(.12,.10,.08) + GC.setLineJoin('bevel') + GC.setLineWidth(14*SCR.k) local W,H=SCR.w,SCR.h - gc.line(.018*W,.567*H,.101*W,.512*H,.202*W,.369*H,.260*W,.212*H) - gc.line(.247*W,.257*H,.307*W,.383*H,.352*W,.436*H,.401*W,.309*H) - gc.line(.982*W,.567*H,.899*W,.512*H,.798*W,.369*H,.740*W,.212*H) - gc.line(.753*W,.257*H,.693*W,.383*H,.648*W,.436*H,.599*W,.309*H) + GC.line(.018*W,.567*H,.101*W,.512*H,.202*W,.369*H,.260*W,.212*H) + GC.line(.247*W,.257*H,.307*W,.383*H,.352*W,.436*H,.401*W,.309*H) + GC.line(.982*W,.567*H,.899*W,.512*H,.798*W,.369*H,.740*W,.212*H) + GC.line(.753*W,.257*H,.693*W,.383*H,.648*W,.436*H,.599*W,.309*H) local k=SCR.k for i=1,8 do - gc.setColor(wingColor[i]) + GC.setColor(wingColor[i]) local B=crystals[i] - gc.draw(crystal_img,B.x,B.y,B.a,k,k,21,0) + GC.draw(crystal_img,B.x,B.y,B.a,k,k,21,0) B=crystals[8+i] - gc.draw(crystal_img,B.x,B.y,B.a,-k,k,21,0) + GC.draw(crystal_img,B.x,B.y,B.a,-k,k,21,0) end end function back.event(level) for i=1,8 do local B=crystals[i] - B.va=B.va+.001*level*(1+rnd()) + B.va=B.va+.001*level*(1+math.random()) B=crystals[17-i] - B.va=B.va-.001*level*(1+rnd()) + B.va=B.va-.001*level*(1+math.random()) end end function back.discard() diff --git a/parts/bot/bot_9s.lua b/parts/bot/bot_9s.lua index b14b9b7d6..5f91e2cc4 100644 --- a/parts/bot/bot_9s.lua +++ b/parts/bot/bot_9s.lua @@ -38,8 +38,8 @@ local FCL={ local LclearScore={[0]=0,-200,-150,-100,200} local HclearScore={[0]=0,100,140,200,500} local function _ifoverlapAI(f,bk,x,y) - for i=1,#bk do for j=1,#bk[1]do - if f[y+i-1]and bk[i][j]and f[y+i-1][x+j-1]>0 then + for i=1,#bk do for j=1,#bk[1] do + if f[y+i-1] and bk[i][j] and f[y+i-1][x+j-1]>0 then return true end end end @@ -65,16 +65,21 @@ local function _getScore(field,cb,cy) local hole=0 for i=cy+#cb-1,cy,-1 do + local full=true for j=1,10 do if field[i][j]==0 then - goto CONTINUE_notFull + -- goto CONTINUE_notFull + full=false + break end end - discardRow(rem(field,i)) - clear=clear+1 - ::CONTINUE_notFull:: + if full then + -- ::CONTINUE_notFull:: + discardRow(rem(field,i)) + clear=clear+1 + end end - if #field==0 then--PC + if #field==0 then-- PC return 1e99 end for x=1,10 do @@ -116,10 +121,10 @@ local function _getScore(field,cb,cy) -#field*30 -#cb*15 +(#field>10 and - HclearScore[clear]--Clearing - -hole*70--Hole - -cy*50--Height - -sdh--Sum of DeltaH + HclearScore[clear]-- Clearing + -hole*70-- Hole + -cy*50-- Height + -sdh-- Sum of DeltaH or LclearScore[clear] -hole*100 @@ -139,10 +144,10 @@ local bot_9s={} function bot_9s.thread(bot) local P,data,keys=bot.P,bot.data,bot.keys while true do - --Thinking + -- Thinking yield() - local Tfield={}--Test field - local best={x=1,dir=0,hold=false,score=-1e99}--Best method + local Tfield={}-- Test field + local best={x=1,dir=0,hold=false,score=-1e99}-- Best method local field_org=P.field for i=1,#field_org do Tfield[i]=getRow(0) @@ -152,33 +157,33 @@ function bot_9s.thread(bot) end for ifhold=0,data.hold and P.gameEnv.holdCount>0 and 1 or 0 do - --Get block id + -- Get block id local bn if ifhold==0 then bn=P.cur and P.cur.id else - bn=P.holdQueue[1]and P.holdQueue[1].id or P.nextQueue[1]and P.nextQueue[1].id + bn=P.holdQueue[1] and P.holdQueue[1].id or P.nextQueue[1] and P.nextQueue[1].id end if bn then - for dir=0,dirCount[bn]do--Each dir + for dir=0,dirCount[bn] do-- Each dir local cb=BLOCKS[bn][dir] - for cx=1,11-#cb[1]do--Each pos + for cx=1,11-#cb[1] do-- Each pos local cy=#Tfield+1 - --Move to bottom - while cy>1 and not _ifoverlapAI(Tfield,cb,cx,cy-1)do + -- Move to bottom + while cy>1 and not _ifoverlapAI(Tfield,cb,cx,cy-1) do cy=cy-1 end - --Simulate lock + -- Simulate lock for i=1,#cb do local y=cy+i-1 - if not Tfield[y]then + if not Tfield[y] then Tfield[y]=getRow(0) end local L=Tfield[y] - for j=1,#cb[1]do - if cb[i][j]then + for j=1,#cb[1] do + if cb[i][j] then L[cx+j-1]=1 end end @@ -194,7 +199,7 @@ function bot_9s.thread(bot) end if not best.bn then return 1 end - --Release cache + -- Release cache while #Tfield>0 do discardRow(rem(Tfield,1)) end @@ -207,7 +212,7 @@ function bot_9s.thread(bot) end ins(keys,6) - --Check if time to change target + -- Check if time to change target yield() if P.aiRND:random()<.00126 then P:changeAtkMode(P.aiRND:random()<.85 and 1 or #P.atker>3 and 4 or P.aiRND:random()<.3 and 2 or 3) diff --git a/parts/bot/bot_cc.lua b/parts/bot/bot_cc.lua index da6bde475..21abac02e 100644 --- a/parts/bot/bot_cc.lua +++ b/parts/bot/bot_cc.lua @@ -11,9 +11,9 @@ function bot_cc:checkDest(b2b,atk,exblock,yomi) local dest=self.P.destFX if not dest then return end if not (dest.b2b==b2b and dest.attack==atk and dest.extra==exblock) then - print(('hope: %s %s %s'):format(dest.b2b,dest.attack,dest.extra)) - print(('real: %s %s %s'):format(b2b,atk,exblock)) - print(yomi) + -- print(('hope: %s %s %s'):format(dest.b2b,dest.attack,dest.extra)) + -- print(('real: %s %s %s'):format(b2b,atk,exblock)) + -- print(yomi) self:lockWrongPlace() self.P.destFX=nil return @@ -21,18 +21,18 @@ function bot_cc:checkDest(b2b,atk,exblock,yomi) local CB=self.P.cur.bk for k=1,#dest,2 do local r=CB[dest[k+1]-self.P.curY+2] - if not r or not r[dest[k]-self.P.curX+2]then - print('wrong place') + if not r or not r[dest[k]-self.P.curX+2] then + -- print('wrong place') self:lockWrongPlace() self.P.destFX=nil return end end - local should_spawn = self.P:getNextSpawn() - 1 + local should_spawn = self.P:getSpawnY(self.P.nextQueue[1]) - 1 if dest.spawn ~= should_spawn then assert(dest.spawn > should_spawn) - print('wrong spawn: should be '..dest.spawn..' but '..should_spawn) - print('-- should only happen when camera is going down') + -- print('wrong spawn: should be '..dest.spawn..' but '..should_spawn) + -- print('-- should only happen when camera is going down') self:lockWrongPlace() self.P.destFX=nil return @@ -43,18 +43,18 @@ function bot_cc:revive() self.P:loadAI(self.data) end function bot_cc:pushNewNext(id) - self.ccBot:addNext(rem(self.bufferedNexts,1)) ins(self.bufferedNexts,id) + self.ccBot:addNext(rem(self.bufferedNexts,1)) end function bot_cc:thread() local P,keys=self.P,self.keys local ccBot=self.ccBot while true do - --Start thinking + -- Start thinking yield() ccBot:think() - --Poll keys + -- Poll keys local success,result,dest,hold,move,b2b,attack,extra,spawn repeat yield() @@ -73,10 +73,10 @@ function bot_cc:thread() dest.extra = extra dest.spawn = spawn P.destFX=dest - if hold then--Hold + if hold then-- Hold keys[1]=8 end - while move[1]do + while move[1] do local m=rem(move,1) if m<4 then ins(keys,m+1) @@ -89,7 +89,7 @@ function bot_cc:thread() ins(keys,6) end - --Check if time to change target + -- Check if time to change target yield() if P.aiRND:random()<.00126 then P:changeAtkMode(P.aiRND:random()<.85 and 1 or #P.atker>3 and 4 or P.aiRND:random()<.3 and 2 or 3) @@ -104,8 +104,8 @@ function bot_cc:updateField() F[i],i=F0[y][x]>0,i+1 end end while i<=400 do F[i],i=false,i+1 end - local y = P:getNextSpawn()-1 - if not pcall(self.ccBot.reset,self.ccBot,F,P.b2b,P.combo,P.stat.pc,P.stat.row,y)then + local y = P:getSpawnY(P.nextQueue[1])-1 + if not pcall(self.ccBot.reset,self.ccBot,F,P.b2b,P.combo,P.stat.pc,P.stat.row,y) then print("CC is dead ("..P.id..")","error") for y=#F0,1,-1 do local s="" diff --git a/parts/bot/init.lua b/parts/bot/init.lua index 1910f336c..aed19b239 100644 --- a/parts/bot/init.lua +++ b/parts/bot/init.lua @@ -14,9 +14,9 @@ function baseBot.update(bot) local keys=bot.keys if P.control and P.cur then bot.delay=bot.delay-1 - if not keys[1]then + if not keys[1] then if bot.runningThread then - if not pcall(bot.runningThread)then + if not pcall(bot.runningThread) then bot.runningThread=false end else @@ -61,18 +61,18 @@ local AISpeed={60,50,42,34,27,21,16,12,9,6} } ]] function BOT.template(arg) - if arg.type=='CC'then - return{ + if arg.type=='CC' then + return { type='CC', next=arg.next, hold=arg.hold, delay=AISpeed[arg.speedLV], node=arg.node, - bag=(arg.randomizer or'bag')=='bag', + bag=(arg.randomizer or 'bag')=='bag', _20G=arg._20G, } - elseif arg.type=='9S'then - return{ + elseif arg.type=='9S' then + return { type='9S', delay=math.floor(AISpeed[arg.speedLV]), hold=arg.hold, @@ -82,7 +82,7 @@ end function BOT.new(P,data) local bot={P=P,data=data} - if data.type=="CC"then + if data.type=="CC" then P:setRS('TRS') bot.keys={} bot.bufferedNexts={} @@ -92,7 +92,7 @@ function BOT.new(P,data) P:setHold(1) end - local cc=cc or REQUIRE"CCloader" + local cc=REQUIRE"CCloader" if not cc then data.type=false return BOT.new(P,data) @@ -107,8 +107,8 @@ function BOT.new(P,data) local cc_lua=require"parts.bot.bot_cc" setmetatable(bot,{__index=function(self,k) return - self.ccBot[k]and function(_,...)self.ccBot[k](self.ccBot,...)end or - cc_lua[k]and function(_,...)cc_lua[k](self,...)end or + self.ccBot[k] and function(_,...)self.ccBot[k](self.ccBot,...) end or + cc_lua[k] and function(_,...)cc_lua[k](self,...) end or assert(baseBot[k],"No CC action called "..k) end}) @@ -127,7 +127,7 @@ function BOT.new(P,data) end bot.runningThread=coroutine.wrap(cc_lua.thread) bot.runningThread(bot) - else--if data.type=="9S"then--9s or else + else-- if data.type=="9S" then-- 9s or else TABLE.cover(baseBot,bot) TABLE.cover(require"parts.bot.bot_9s",bot) P:setRS('TRS') diff --git a/parts/char.lua b/parts/char.lua index dbefe33ca..4cbd3dc0c 100644 --- a/parts/char.lua +++ b/parts/char.lua @@ -1,5 +1,5 @@ local L={ - zChan={--F0000~F003F + zChan={-- F0000 - F003F none= 0xF0000, normal= 0xF0001, full= 0xF0002, @@ -15,7 +15,7 @@ local L={ angry= 0xF000C, shocked= 0xF000D, ellipses= 0xF000E, - sweat_drop= 0xF000F, + sweatDrop= 0xF000F, cry= 0xF0010, cracked= 0xF0011, qualified= 0xF0012, @@ -24,7 +24,7 @@ local L={ thinking= 0xF0015, spark= 0xF0016, }, - mino={--F0040~F007F + mino={-- F0040 - F007F Z=0xF0040, S=0xF0041, J=0xF0042, @@ -57,7 +57,7 @@ local L={ I2=0xF005B, O1=0xF005C, }, - icon={--F0080~F00FF + icon={-- F0080 - F00FF menu= 0xF0080, music= 0xF0081, language= 0xF0082, @@ -130,8 +130,36 @@ local L={ bomb= 0xF00C5, garbage= 0xF00C6, copy= 0xF00C7, + tas= 0xF00C8, + pencil= 0xF00C9, + magGlass= 0xF00CA, + zoomIn= 0xF00CB, + zoomOut= 0xF00CC, + zoomDefault= 0xF00CD, + share= 0xF00CE, + save= 0xF00CF, + fastForward= 0xF00D0, + rewind= 0xF00D1, + nextSong= 0xF00D2, + previousSong= 0xF00D3, + cycle= 0xF00D4, + cycleOne= 0xF00D5, + cycleOff= 0xF00D6, + random= 0xF00D7, + randomOff= 0xF00D8, + randomAuto= 0xF00D9, + closedCaption= 0xF00DA, + fullBeat= 0xF00DB, + rewind10= 0xF00DC, + rewind30= 0xF00DD, + foward10= 0xF00DE, + foward30= 0xF00DF, + fontUp= 0xF00E0, + fontDown= 0xF00E1, + erase= 0xF00E2, + auto= 0xF00E3, }, - key={--F0100~F017F + key={-- F0100 - F017F macCmd= 0xF0100, macOpt= 0xF0101, macCtrl= 0xF0102, @@ -172,7 +200,7 @@ local L={ macPgdnAlt= 0xF0125, iecPower= 0xF0126, }, - controller={--F0180~F01FF + controller={-- F0180 - F01FF xbox= 0xF0180, lt= 0xF0181, rt= 0xF0182, @@ -186,12 +214,12 @@ local L={ joystickR= 0xF018A, jsLU= 0xF018B, jsLD= 0xF018C, - jsLL= 0xF018D, - jsLR= 0xF018E, + jsLR= 0xF018D, + jsLL= 0xF018E, jsRU= 0xF018F, jsRD= 0xF0190, - jsRL= 0xF0191, - jsRR= 0xF0192, + jsRR= 0xF0191, + jsRL= 0xF0192, jsLPress= 0xF0193, jsRPress= 0xF0194, dpad= 0xF0195, @@ -212,7 +240,7 @@ local L={ psCreate= 0xF01A4, psOption= 0xF01A5, }, - mahjong={--F0200~F027F + mahjong={-- F0200 - F027F m1= 0xF0200, m2= 0xF0201, m3= 0xF0202, @@ -299,6 +327,67 @@ local L={ s1j= 0xF0253, s1jBase= 0xF0254, s1jComb= 0xF0255, + }, + cards={-- F0300 - F070F + cardBack= 0xF0300, + spadeA= 0xF0301, + spade2= 0xF0302, + spade3= 0xF0303, + spade4= 0xF0304, + spade5= 0xF0305, + spade6= 0xF0306, + spade7= 0xF0307, + spade8= 0xF0308, + spade9= 0xF0309, + spade10= 0xF030A, + spadeJ= 0xF030B, + spadeC= 0xF030C, + spadeQ= 0xF030D, + spadeK= 0xF030E, + heartA= 0xF0401, + heart2= 0xF0402, + heart3= 0xF0403, + heart4= 0xF0404, + heart5= 0xF0405, + heart6= 0xF0406, + heart7= 0xF0407, + heart8= 0xF0408, + heart9= 0xF0409, + heart10= 0xF040A, + heartJ= 0xF040B, + heartC= 0xF040C, + heartQ= 0xF040D, + heartK= 0xF040E, + diamondA= 0xF0501, + diamond2= 0xF0502, + diamond3= 0xF0503, + diamond4= 0xF0504, + diamond5= 0xF0505, + diamond6= 0xF0506, + diamond7= 0xF0507, + diamond8= 0xF0508, + diamond9= 0xF0509, + diamond10= 0xF050A, + diamondJ= 0xF050B, + diamondC= 0xF050C, + diamondQ= 0xF050D, + diamondK= 0xF050E, + jokerBlack= 0xF050F, + clubA= 0xF0601, + club2= 0xF0602, + club3= 0xF0603, + club4= 0xF0604, + club5= 0xF0605, + club6= 0xF0606, + club7= 0xF0607, + club8= 0xF0608, + club9= 0xF0609, + club10= 0xF060A, + clubJ= 0xF060B, + clubC= 0xF060C, + clubQ= 0xF060D, + clubK= 0xF060E, + jokerWhite= 0xF060F, } } diff --git a/parts/customEnv0.lua b/parts/customEnv0.lua index 075304d0f..5b6629257 100644 --- a/parts/customEnv0.lua +++ b/parts/customEnv0.lua @@ -1,7 +1,7 @@ -return{ +return { version=VERSION.code, - --Basic + -- Basic drop=1e99, lock=1e99, wait=0, @@ -9,17 +9,17 @@ return{ hang=5, hurry=1e99, - --Control + -- Control nextCount=6, holdMode='hold', holdCount=1, infHold=true, phyHold=false, - --Visual + -- Visual bone=false, - --Rule + -- Rule sequence='bag', lockout=false, fieldH=20, @@ -40,7 +40,7 @@ return{ garbageSpeed=1, missionKill=false, - --Else + -- Else bg='blockrain', bgm='hang out', diff --git a/parts/data.lua b/parts/data.lua index 33c25864f..444d57000 100644 --- a/parts/data.lua +++ b/parts/data.lua @@ -1,25 +1,25 @@ -local int=math.floor +local floor=math.floor local char,byte=string.char,string.byte local ins=table.insert -local BAG,FIELD,MISSION,CUSTOMENV,GAME=BAG,FIELD,MISSION,CUSTOMENV,GAME +local GAME=GAME local DATA={} ---Sep symbol: 33 (!) ---Safe char: 34~126 +-- Sep symbol: 33 (!) +-- Safe char: 34~126 --[[ Count: 34~96 Block: 97~125 Encode: A[B] sequence, A = block ID, B = repeat times, no B means do not repeat. Example: "abcdefg" is [SZJLTOI], "a^aDb)" is [Z*63,Z*37,S*10] ]] -function DATA.copySequence() +function DATA.copySequence(bag) local str="" local count=1 - for i=1,#BAG+1 do - if BAG[i+1]~=BAG[i]or count==64 then - str=str..char(96+BAG[i]) + for i=1,#bag+1 do + if bag[i+1]~=bag[i] or count==64 then + str=str..char(96+bag[i]) if count>1 then str=str..char(32+count) count=1 @@ -32,7 +32,7 @@ function DATA.copySequence() return str end function DATA.pasteSequence(str) - TABLE.cut(BAG) + local bag={} local b,reg for i=1,#str do b=byte(str,i) @@ -44,20 +44,20 @@ function DATA.pasteSequence(str) end else if b>=97 and b<=125 then - ins(BAG,reg) + ins(bag,reg) reg=b-96 elseif b>=34 and b<=96 then for _=1,b-32 do - ins(BAG,reg) + ins(bag,reg) end reg=false end end end if reg then - ins(BAG,reg) + ins(bag,reg) end - return true + return true,bag end local fieldMeta={__index=function(self,h) @@ -66,14 +66,13 @@ local fieldMeta={__index=function(self,h) end return self[h] end} -function DATA.newBoard(f)--Generate a new board - return setmetatable(f and TABLE.shift(f)or{},fieldMeta) +function DATA.newBoard(f)-- Generate a new board + return setmetatable(f and TABLE.shift(f) or{},fieldMeta) end -function DATA.copyBoard(page)--Copy the [page] board - local F=FIELD[page or 1] +function DATA.copyBoard(F)-- Copy the [page] board local str="" - --Encode field + -- Encode field for y=1,#F do local S="" local L=F[y] @@ -84,32 +83,27 @@ function DATA.copyBoard(page)--Copy the [page] board end return STRING.packBin(str) end -function DATA.copyBoards() +function DATA.copyBoards(field) local out={} - for i=1,#FIELD do - out[i]=DATA.copyBoard(i) + for i=1,#field do + out[i]=DATA.copyBoard(field[i]) end return table.concat(out,"!") end -function DATA.pasteBoard(str,page)--Paste [str] data to [page] board - if not page then - page=1 - end - if not FIELD[page]then - FIELD[page]=DATA.newBoard() - end - local F=FIELD[page] +function DATA.pasteBoard(str)-- Paste [str] data to [page] board + local F=DATA.newBoard() - --Decode + -- Decode str=STRING.unpackBin(str) if not str then return end - local fX,fY=1,1--*ptr for Field(r*10+(c-1)) + local fX,fY=1,1-- *ptr for Field(r*10+(c-1)) local p=1 + local lineLimit=126 while true do - local b=byte(str,p)--1byte + local b=byte(str,p)-- 1byte - --Str end + -- Str end if not b then if fX~=1 then return @@ -118,22 +112,22 @@ function DATA.pasteBoard(str,page)--Paste [str] data to [page] board end end - local id=b%32-1--Block id - if id>26 then return end--Illegal blockid - b=int(b/32)--Mode id + local id=b%32-1-- Block id + if id>26 then return end-- Illegal blockid + b=floor(b/32)-- Mode id F[fY][fX]=id if fX<10 then fX=fX+1 else fY=fY+1 - if fY>20 then break end + if fY>lineLimit then break end fX=1 end p=p+1 end - return true + return true,F,#str>lineLimit*10 end --[[ @@ -152,14 +146,14 @@ end O1=61,O2=62,O3=63,O4=64, I1=71,I2=72,I3=73,I4=74, ]] -function DATA.copyMission() +function DATA.copyMission(mission) local _ local str="" local count=1 - for i=1,#MISSION+1 do - if MISSION[i+1]~=MISSION[i]or count==13 then - _=33+MISSION[i] + for i=1,#mission+1 do + if mission[i+1]~=mission[i] or count==13 then + _=33+mission[i] str=str..char(_) if count>1 then str=str..char(113+count) @@ -174,7 +168,7 @@ function DATA.copyMission() end function DATA.pasteMission(str) local b - TABLE.cut(MISSION) + local mission={} local reg for i=1,#str do b=byte(str,i) @@ -186,46 +180,69 @@ function DATA.pasteMission(str) end else if b>=34 and b<=114 then - if ENUM_MISSION[reg]then - ins(MISSION,reg) + if ENUM_MISSION[reg] then + ins(mission,reg) reg=b-33 else - TABLE.cut(MISSION) + TABLE.cut(mission) return end elseif b>=115 and b<=126 then for _=1,b-113 do - ins(MISSION,reg) + ins(mission,reg) end reg=false end end end if reg then - ins(MISSION,reg) + ins(mission,reg) end - return true + return true,mission end -function DATA.copyQuestArgs() - local ENV=CUSTOMENV +function DATA.copyQuestArgs(custom_env) + local ENV=custom_env local str="".. ENV.holdCount.. - (ENV.ospin and"O"or"Z").. - (ENV.missionKill and"M"or"Z").. + (ENV.ospin and "O" or "Z").. + (ENV.missionKill and "M" or "Z").. ENV.sequence return str end function DATA.pasteQuestArgs(str) if #str<4 then return end - local ENV=CUSTOMENV - ENV.holdCount= str:byte(1)-48 + local ENV={} + ENV.holdCount= MATH.clamp(str:byte(1)-48,0,26) ENV.ospin= str:byte(2)~=90 ENV.missionKill=str:byte(3)~=90 ENV.sequence= str:sub(4) - return true + if select(2,require"parts.player.seqGenerators"(ENV.sequence)) then + MES.new('warn',text.invalidSequence) + ENV.sequence='bag' + end + return true,ENV end +local function _encode(t) + if t<128 then return char(t) end + local buffer2=char(t%128) + t=floor(t/128) + while t>=128 do + buffer2=char(128+t%128)..buffer2 + t=floor(t/128) + end + return char(128+t)..buffer2 +end +local function _decode(str,p) + local ret=0 + repeat + local b=byte(str,p) + p=p+1 + ret=ret*128+(b<128 and b or b-128) + until b<128 + return ret,p +end --[[ Replay file: a zlib-compressed json table @@ -239,126 +256,72 @@ end large value will be encoded as 1xxxxxxx(high)-1xxxxxxx-...-0xxxxxxx(low) Example (decoded): - 6,1, 20,-1, 0,2, 26,-2, 872,4, ... + 26,1, 42,-1, ... This means: - Press key1 at 6f - Release key1 at 26f (6+20) - Press key2 at the same time (26+0) - Release key 2 after 26 frame (26+26) - Press key 4 after 872 frame (52+872) + Press key1 at 26f + Release key1 at 42f ... ]] function DATA.dumpRecording(list,ptr) local out="" - local buffer,buffer2="" + local buffer="" if not ptr then ptr=1 end - local prevFrm=list[ptr-2]or 0 - while list[ptr]do - --Flush buffer - if #buffer>10 then + while list[ptr] do + if #buffer>26 then out=out..buffer buffer="" end - - --Encode time - local t=list[ptr]-prevFrm - prevFrm=list[ptr] - if t>=128 then - buffer2=char(t%128) - t=int(t/128) - while t>=128 do - buffer2=char(128+t%128)..buffer2 - t=int(t/128) - end - buffer=buffer..char(128+t)..buffer2 - else - buffer=buffer..char(t) - end - - --Encode event - t=list[ptr+1] - if t>=128 then - buffer2=char(t%128) - t=int(t/128) - while t>=128 do - buffer2=char(128+t%128)..buffer2 - t=int(t/128) - end - buffer=buffer..char(128+t)..buffer2 - else - buffer=buffer..char(t) - end - - --Step - ptr=ptr+2 + buffer=buffer.._encode(list[ptr]) + ptr=ptr+1 end return out..buffer,ptr end function DATA.pumpRecording(str,L) local len=#str local p=1 + local data - local curFrm=L[#L-1]or 0 - local code while p<=len do - --Read delta time - code=0 - local b=byte(str,p) - while b>=128 do - code=code*128+b-128 - p=p+1 - b=byte(str,p) - end - curFrm=curFrm+code*128+b - L[#L+1]=curFrm - p=p+1 - - local event=0 - b=byte(str,p) - while b>=128 do - event=event*128+b-128 - p=p+1 - b=byte(str,p) - end - L[#L+1]=event*128+b - p=p+1 + data,p=_decode(str,p) + ins(L,data) end end -do--function DATA.saveReplay() - local noRecList={"custom","solo","round","techmino"} +do-- function DATA.saveReplay() local function _getModList() local res={} - for _,v in next,GAME.mod do - if v.sel>0 then - ins(res,{v.no,v.sel}) + for number,sel in next,GAME.mod do + if sel>0 then + ins(res,{MODOPT[number].no,sel}) end end return res end function DATA.saveReplay() - --Filtering modes that cannot be saved - for _,v in next,noRecList do - if GAME.curModeName:find(v)then - MES.new('error',"Cannot save recording of this mode now!") - return - end + -- Filtering modes that cannot be saved + if GAME.initPlayerCount~=1 then + MES.new('error',"Cannot save recording of more than 1 player now!") + return end - --Write file + -- Write file local fileName=os.date("replay/%Y_%m_%d_%H%M%S.rep") - if not love.filesystem.getInfo(fileName)then + if not love.filesystem.getInfo(fileName) then + local metadata={ + date=os.date("%Y/%m/%d %H:%M:%S"), + mode=GAME.curModeName, + version=VERSION.string, + player=USERS.getUsername(USER.uid), + seed=GAME.seed, + setting=GAME.setting, + mod=_getModList(), + tasUsed=GAME.tasUsed, + } + if GAME.curMode.savePrivate then + metadata.private=GAME.curMode.savePrivate() + end love.filesystem.write(fileName, love.data.compress('string','zlib', - JSON.encode{ - date=os.date("%Y/%m/%d %H:%M:%S"), - mode=GAME.curModeName, - version=VERSION.string, - player=USERS.getUsername(USER.uid), - seed=GAME.seed, - setting=GAME.setting, - mod=_getModList(), - tasUsed=GAME.tasUsed, - }.."\n".. + JSON.encode(metadata).."\n".. DATA.dumpRecording(GAME.rep) ) ) @@ -371,29 +334,33 @@ do--function DATA.saveReplay() end function DATA.parseReplay(fileName,ifFull) local fileData - --Read file + -- Read file fileData=love.filesystem.read(fileName) return DATA.parseReplayData(fileName,fileData,ifFull) end function DATA.parseReplayData(fileName,fileData,ifFull) - local success,metaData,rep + local success,metaData + local rep={-- unavailable replay object + fileName=fileName, + available=false, + } - if not(fileData and #fileData>0)then goto BREAK_cannotParse end + if not (fileData and #fileData>0) then return rep end-- goto BREAK_cannotParse - --Decompress file + -- Decompress file success,fileData=pcall(love.data.decompress,'string','zlib',fileData) - if not success then goto BREAK_cannotParse end + if not success then return rep end-- goto BREAK_cannotParse - --Load metadata + -- Load metadata metaData,fileData=STRING.readLine(fileData) metaData=JSON.decode(metaData) - if not metaData then goto BREAK_cannotParse end + if not metaData then return rep end-- goto BREAK_cannotParse - --Convert ancient replays - metaData.mode=MODE_UPDATE_MAP[metaData.mode]or metaData.mode - if not MODES[metaData.mode]then goto BREAK_cannotParse end + -- Convert ancient replays + metaData.mode=MODE_UPDATE_MAP[metaData.mode] or metaData.mode + if not MODES[metaData.mode] then return rep end-- goto BREAK_cannotParse - --Create replay object + -- Create replay object rep={ fileName=fileName, available=true, @@ -409,13 +376,9 @@ function DATA.parseReplayData(fileName,fileData,ifFull) tasUsed=metaData.tasUsed, } if ifFull then rep.data=fileData end - do return rep end - - --Create unavailable replay object - ::BREAK_cannotParse:: - return{ - fileName=fileName, - available=false, - } + if metaData.private then + rep.private=metaData.private + end + return rep end return DATA diff --git a/parts/discordRPC.lua b/parts/discordRPC.lua new file mode 100644 index 000000000..fa16caac4 --- /dev/null +++ b/parts/discordRPC.lua @@ -0,0 +1,331 @@ +if SYSTEM=='Web' then + return {update=NULL} +end + +local appId='1288557386700951554' + +local ffi=require"ffi" + +local RPC_C +if SYSTEM=='Windows' then + local suc + suc,RPC_C=pcall(ffi.load,"discord-rpc") + if not (suc and RPC_C) then + print("Failed to load Discord-RPC lib",RPC_C) + MES.new('error',"Failed to load Discord-RPC lib") + RPC_C=nil + end +end + +local RPC +if RPC_C then + RPC={} + ffi.cdef[[ + typedef struct DiscordRichPresence { + const char* state; /* max 128 bytes */ + const char* details; /* max 128 bytes */ + int64_t startTimestamp; + int64_t endTimestamp; + const char* largeImageKey; /* max 32 bytes */ + const char* largeImageText; /* max 128 bytes */ + const char* smallImageKey; /* max 32 bytes */ + const char* smallImageText; /* max 128 bytes */ + const char* partyId; /* max 128 bytes */ + int partySize; + int partyMax; + const char* matchSecret; /* max 128 bytes */ + const char* joinSecret; /* max 128 bytes */ + const char* spectateSecret; /* max 128 bytes */ + int8_t instance; + } DiscordRichPresence; + + typedef struct DiscordUser { + const char* userId; + const char* username; + const char* discriminator; + const char* avatar; + } DiscordUser; + + typedef void (*readyPtr)(const DiscordUser* request); + typedef void (*disconnectedPtr)(int errorCode, const char* message); + typedef void (*erroredPtr)(int errorCode, const char* message); + typedef void (*joinGamePtr)(const char* joinSecret); + typedef void (*spectateGamePtr)(const char* spectateSecret); + typedef void (*joinRequestPtr)(const DiscordUser* request); + + typedef struct DiscordEventHandlers { + readyPtr ready; + disconnectedPtr disconnected; + erroredPtr errored; + joinGamePtr joinGame; + spectateGamePtr spectateGame; + joinRequestPtr joinRequest; + } DiscordEventHandlers; + + void Discord_Initialize(const char* applicationId, + DiscordEventHandlers* handlers, + int autoRegister, + const char* optionalSteamId); + void Discord_Shutdown(void); + void Discord_RunCallbacks(void); + void Discord_UpdatePresence(const DiscordRichPresence* presence); + void Discord_ClearPresence(void); + void Discord_Respond(const char* userid, int reply); + void Discord_UpdateHandlers(DiscordEventHandlers* handlers); + ]] + + local function unpackDiscordUser(request) + return ffi.string(request.userId),ffi.string(request.username), + ffi.string(request.discriminator),ffi.string(request.avatar) + end + + -- callback proxies + -- note: callbacks are not JIT compiled (= SLOW), try to avoid doing performance critical tasks in them + -- luajit.org/ext_ffi_semantics.html + local ready_proxy=ffi.cast("readyPtr",function(request) + if RPC.ready then + RPC.ready(unpackDiscordUser(request)) + end + end) + + local disconnected_proxy=ffi.cast("disconnectedPtr",function(errorCode,message) + if RPC.disconnected then + RPC.disconnected(errorCode,ffi.string(message)) + end + end) + + local errored_proxy=ffi.cast("erroredPtr",function(errorCode,message) + if RPC.errored then + RPC.errored(errorCode,ffi.string(message)) + end + end) + + local joinGame_proxy=ffi.cast("joinGamePtr",function(joinSecret) + if RPC.joinGame then + RPC.joinGame(ffi.string(joinSecret)) + end + end) + + local spectateGame_proxy=ffi.cast("spectateGamePtr",function(spectateSecret) + if RPC.spectateGame then + RPC.spectateGame(ffi.string(spectateSecret)) + end + end) + + local joinRequest_proxy=ffi.cast("joinRequestPtr",function(request) + if RPC.joinRequest then + RPC.joinRequest(unpackDiscordUser(request)) + end + end) + + -- helpers + local function checkArg(arg,argType,argName,func,maybeNil) + assert(type(arg)==argType or (maybeNil and arg==nil), + string.format("Argument \"%s\" to function \"%s\" has to be of type \"%s\"", + argName,func,argType)) + end + + local function checkStrArg(arg,maxLen,argName,func,maybeNil) + if maxLen then + assert(type(arg)=="string" and arg:len()<=maxLen or (maybeNil and arg==nil), + string.format("Argument \"%s\" of function \"%s\" has to be of type string with maximum length %d", + argName,func,maxLen)) + else + checkArg(arg,"string",argName,func,true) + end + end + + local function checkIntArg(arg,maxBits,argName,func,maybeNil) + maxBits=math.min(maxBits or 32,52) -- lua number (double) can only store integers < 2^53 + local maxVal=2^(maxBits-1) -- assuming signed integers, which, for now, are the only ones in use + assert(type(arg)=="number" and math.floor(arg)==arg + and arg=-maxVal + or (maybeNil and arg==nil), + string.format("Argument \"%s\" of function \"%s\" has to be a whole number <= %d", + argName,func,maxVal)) + end + + -- function wrappers + function RPC.initialize(applicationId,autoRegister,optionalSteamId) + local func="discordRPC.Initialize" + checkStrArg(applicationId,nil,"applicationId",func) + checkArg(autoRegister,"boolean","autoRegister",func) + if optionalSteamId~=nil then + checkStrArg(optionalSteamId,nil,"optionalSteamId",func) + end + + local eventHandlers=ffi.new("struct DiscordEventHandlers") + eventHandlers.ready=ready_proxy + eventHandlers.disconnected=disconnected_proxy + eventHandlers.errored=errored_proxy + eventHandlers.joinGame=joinGame_proxy + eventHandlers.spectateGame=spectateGame_proxy + eventHandlers.joinRequest=joinRequest_proxy + + RPC_C.Discord_Initialize(applicationId,eventHandlers, + autoRegister and 1 or 0,optionalSteamId) + end + + function RPC.shutdown() + RPC_C.Discord_Shutdown() + end + + function RPC.runCallbacks() + RPC_C.Discord_RunCallbacks() + end + -- http://luajit.org/ext_ffi_semantics.html#callback : + -- It is not allowed, to let an FFI call into a C function (runCallbacks) + -- get JIT-compiled, which in turn calls a callback, calling into Lua again (e.g. discordRPC.ready). + -- Usually this attempt is caught by the interpreter first and the C function + -- is blacklisted for compilation. + -- solution: + -- "Then you'll need to manually turn off JIT-compilation with jit.off() for + -- the surrounding Lua function that invokes such a message polling function." + jit.off(RPC.runCallbacks) + + function RPC.updatePresence(presence) + local func="discordRPC.updatePresence" + checkArg(presence,"table","presence",func) + + -- -1 for string length because of 0-termination + checkStrArg(presence.state,127,"presence.state",func,true) + checkStrArg(presence.details,127,"presence.details",func,true) + + checkIntArg(presence.startTimestamp,64,"presence.startTimestamp",func,true) + checkIntArg(presence.endTimestamp,64,"presence.endTimestamp",func,true) + + checkStrArg(presence.largeImageKey,31,"presence.largeImageKey",func,true) + checkStrArg(presence.largeImageText,127,"presence.largeImageText",func,true) + checkStrArg(presence.smallImageKey,31,"presence.smallImageKey",func,true) + checkStrArg(presence.smallImageText,127,"presence.smallImageText",func,true) + checkStrArg(presence.partyId,127,"presence.partyId",func,true) + + checkIntArg(presence.partySize,32,"presence.partySize",func,true) + checkIntArg(presence.partyMax,32,"presence.partyMax",func,true) + + checkStrArg(presence.matchSecret,127,"presence.matchSecret",func,true) + checkStrArg(presence.joinSecret,127,"presence.joinSecret",func,true) + checkStrArg(presence.spectateSecret,127,"presence.spectateSecret",func,true) + + checkIntArg(presence.instance,8,"presence.instance",func,true) + + local cpresence=ffi.new("struct DiscordRichPresence") + cpresence.state=presence.state + cpresence.details=presence.details + cpresence.startTimestamp=presence.startTimestamp or 0 + cpresence.endTimestamp=presence.endTimestamp or 0 + cpresence.largeImageKey=presence.largeImageKey + cpresence.largeImageText=presence.largeImageText + cpresence.smallImageKey=presence.smallImageKey + cpresence.smallImageText=presence.smallImageText + cpresence.partyId=presence.partyId + cpresence.partySize=presence.partySize or 0 + cpresence.partyMax=presence.partyMax or 0 + cpresence.matchSecret=presence.matchSecret + cpresence.joinSecret=presence.joinSecret + cpresence.spectateSecret=presence.spectateSecret + cpresence.instance=presence.instance or 0 + + RPC_C.Discord_UpdatePresence(cpresence) + end + + function RPC.clearPresence() + RPC_C.Discord_ClearPresence() + end + + local replyMap={ + no=0, + yes=1, + ignore=2, + } + + -- maybe let reply take ints too (0, 1, 2) and add constants to the module + function RPC.respond(userId,reply) + checkStrArg(userId,nil,"userId","discordRPC.respond") + assert(replyMap[reply],"Argument 'reply' to discordRPC.respond must be 'yes'|'no'|'ignore'") + RPC_C.Discord_Respond(userId,replyMap[reply]) + end + + -- garbage collection callback + RPC.gcDummy=newproxy(true) + getmetatable(RPC.gcDummy).__gc=function() + RPC.shutdown() + ready_proxy:free() + disconnected_proxy:free() + errored_proxy:free() + joinGame_proxy:free() + spectateGame_proxy:free() + joinRequest_proxy:free() + end + + function RPC.ready(userId,username,discriminator,avatar) + print(string.format("Discord: ready (%s,%s,%s,%s)",userId,username,discriminator,avatar)) + end + function RPC.disconnected(errorCode,message) + print(string.format("Discord: disconnected (%d: %s)",errorCode,message)) + end + function RPC.errored(errorCode,message) + print(string.format("Discord: error (%d: %s)",errorCode,message)) + end + function RPC.joinGame(joinSecret) + print(string.format("Discord: join (%s)",joinSecret)) + end + function RPC.spectateGame(spectateSecret) + print(string.format("Discord: spectate (%s)",spectateSecret)) + end + function RPC.joinRequest(userId,username,discriminator,avatar) + print(string.format("Discord: join request (%s,%s,%s,%s)",userId,username,discriminator,avatar)) + RPC.respond(userId,'yes') + end + RPC.initialize(appId,true) +end + +local MyRPC={ + C=RPC_C, + RPC=RPC, + presence={ + startTimestamp=os.time(), + state="Loading...", + details="", + largeImageKey='', + largeImageText="Techmino", + smallImageKey='', + smallImageText="", + }, +} + +---@class DiscordRPC.presence +---@field state? string +---@field details? string +---@field startTimestamp? number +---@field endTimestamp? number +---@field largeImageKey? string +---@field largeImageText? string +---@field smallImageKey? string +---@field smallImageText? string +---@field partyId? string +---@field partySize? number +---@field partyMax? number +---@field matchSecret? string +---@field joinSecret? string +---@field spectateSecret? string +---@field instance? number + +---@overload fun() +---@overload fun(state: DiscordRPC.presence) +---@param state string +---@param details string +function MyRPC.update(state,details) + if state then + for k,v in next, + type(state)=='string' + and {state=state,details=details} + or state + do + MyRPC.presence[k]=v + end + end + if RPC then RPC.updatePresence(MyRPC.presence) end +end + +return MyRPC diff --git a/parts/eventsets/attacker_h.lua b/parts/eventsets/attacker_h.lua index 06c88544a..fa2efa3fd 100644 --- a/parts/eventsets/attacker_h.lua +++ b/parts/eventsets/attacker_h.lua @@ -1,25 +1,25 @@ -return{ +return { mesDisp=function(P) setFont(55) - mStr(P.modeData.wave,63,200) - mStr("22",63,320) + GC.mStr(P.modeData.wave,63,200) + GC.mStr("22",63,320) mText(TEXTOBJ.wave,63,260) mText(TEXTOBJ.nextWave,63,380) end, task=function(P) while true do - YIELD() + coroutine.yield() if P.control and P.atkBufferSum==0 then local D=P.modeData if D.wave==50 then P:win('finish') else if D.wave<20 then - local t=1500-30*D.wave--1500~900 + local t=1500-30*D.wave-- 1500~900 table.insert(P.atkBuffer,{line=generateLine(P.holeRND:random(4,7)),amount=12,countdown=t,cd0=t,time=0,sent=false,lv=3}) table.insert(P.atkBuffer,{line=generateLine(P.holeRND:random(3,8)),amount=10,countdown=t,cd0=t,time=0,sent=false,lv=4}) else - local t=900-10*(D.wave-20)--900~600 + local t=900-10*(D.wave-20)-- 900~600 table.insert(P.atkBuffer,{line=generateLine(P.holeRND:random(10)),amount=14,countdown=t,cd0=t,time=0,sent=false,lv=4}) table.insert(P.atkBuffer,{line=generateLine(P.holeRND:random(4,7)),amount=8,countdown=t,cd0=t,time=0,sent=false,lv=5}) end diff --git a/parts/eventsets/attacker_u.lua b/parts/eventsets/attacker_u.lua index b59da766b..c82814bac 100644 --- a/parts/eventsets/attacker_u.lua +++ b/parts/eventsets/attacker_u.lua @@ -1,21 +1,21 @@ -return{ +return { mesDisp=function(P) setFont(55) - mStr(P.modeData.wave,63,200) - mStr(20+4*math.min(math.floor(P.modeData.wave/10),2),63,320) + GC.mStr(P.modeData.wave,63,200) + GC.mStr(20+4*math.min(math.floor(P.modeData.wave/10),2),63,320) mText(TEXTOBJ.wave,63,260) mText(TEXTOBJ.nextWave,63,380) end, task=function(P) while true do - YIELD() + coroutine.yield() if P.control and P.atkBufferSum<4 then local D=P.modeData if D.wave==50 then P:win('finish') else local s - local t=800-10*D.wave--800~700~600~500 + local t=800-10*math.min(D.wave,30)-- 800~700~600~500 if D.wave<10 then table.insert(P.atkBuffer,{line=generateLine(P.holeRND:random(5,6)),amount=9,countdown=t,cd0=t,time=0,sent=false,lv=3}) table.insert(P.atkBuffer,{line=generateLine(P.holeRND:random(4,7)),amount=11,countdown=t,cd0=t+62,time=0,sent=false,lv=4}) diff --git a/parts/eventsets/backfire_0.lua b/parts/eventsets/backfire_0.lua index 8c4a012cc..621df23d9 100644 --- a/parts/eventsets/backfire_0.lua +++ b/parts/eventsets/backfire_0.lua @@ -1,4 +1,4 @@ -return{ +return { hook_drop=function(P) if P.lastPiece.atk>0 then P:receive(nil,P.lastPiece.atk,0,generateLine(P.holeRND:random(10))) diff --git a/parts/eventsets/backfire_120.lua b/parts/eventsets/backfire_120.lua index 4a284a48a..639cf3b70 100644 --- a/parts/eventsets/backfire_120.lua +++ b/parts/eventsets/backfire_120.lua @@ -1,4 +1,4 @@ -return{ +return { hook_drop=function(P) if P.lastPiece.atk>0 then P:receive(nil,P.lastPiece.atk,120,generateLine(P.holeRND:random(10))) diff --git a/parts/eventsets/backfire_30.lua b/parts/eventsets/backfire_30.lua index bb949c4af..4ae0feb81 100644 --- a/parts/eventsets/backfire_30.lua +++ b/parts/eventsets/backfire_30.lua @@ -1,4 +1,4 @@ -return{ +return { hook_drop=function(P) if P.lastPiece.atk>0 then P:receive(nil,P.lastPiece.atk,30,generateLine(P.holeRND:random(10))) diff --git a/parts/eventsets/backfire_60.lua b/parts/eventsets/backfire_60.lua index 22e7ff042..617fd6243 100644 --- a/parts/eventsets/backfire_60.lua +++ b/parts/eventsets/backfire_60.lua @@ -1,4 +1,4 @@ -return{ +return { hook_drop=function(P) if P.lastPiece.atk>0 then P:receive(nil,P.lastPiece.atk,60,generateLine(P.holeRND:random(10))) diff --git a/parts/eventsets/bigWallGen.lua b/parts/eventsets/bigWallGen.lua index 072406732..a70051117 100644 --- a/parts/eventsets/bigWallGen.lua +++ b/parts/eventsets/bigWallGen.lua @@ -1,4 +1,4 @@ -return{ +return { hook_drop=function(P) if P.lastPiece.row>0 then for _=1,#P.clearedRow do diff --git a/parts/eventsets/blindMesDisp.lua b/parts/eventsets/blindMesDisp.lua index 2dae20072..ecde9fc0c 100644 --- a/parts/eventsets/blindMesDisp.lua +++ b/parts/eventsets/blindMesDisp.lua @@ -1,13 +1,11 @@ -local gc=love.graphics - -return{ +return { mesDisp=function(P) mText(TEXTOBJ.techrash,63,420) setFont(75) - mStr(P.stat.clears[4],63,340) + GC.mStr(P.stat.clears[4],63,340) PLY.draw.applyField(P) - gc.setColor(1,1,1,.1) - gc.draw(IMG.electric,0,106,0,2.6) - PLY.draw.cancelField(P) + GC.setColor(1,1,1,.1) + GC.draw(IMG.electric,0,106,0,2.6) + PLY.draw.cancelField() end, } diff --git a/parts/eventsets/c4wBase.lua b/parts/eventsets/c4wBase.lua index 15fa311a3..3d88cab06 100644 --- a/parts/eventsets/c4wBase.lua +++ b/parts/eventsets/c4wBase.lua @@ -1,10 +1,10 @@ local rem=table.remove -return{ +return { mesDisp=function(P) setFont(45) - mStr(P.combo,63,310) - mStr(P.modeData.maxCombo,63,400) + GC.mStr(P.combo,63,310) + GC.mStr(P.modeData.maxCombo,63,400) mText(TEXTOBJ.combo,63,358) mText(TEXTOBJ.maxcmb,63,450) end, diff --git a/parts/eventsets/c4wCheck_easy.lua b/parts/eventsets/c4wCheck_easy.lua index 5f81dd243..2058cf8d2 100644 --- a/parts/eventsets/c4wCheck_easy.lua +++ b/parts/eventsets/c4wCheck_easy.lua @@ -1,4 +1,4 @@ -return{ +return { hook_drop=function(P) if P.lastPiece.row>0 then for _=1,#P.clearedRow do diff --git a/parts/eventsets/c4wCheck_hard.lua b/parts/eventsets/c4wCheck_hard.lua index e2ecc4d88..ac31d3e8c 100644 --- a/parts/eventsets/c4wCheck_hard.lua +++ b/parts/eventsets/c4wCheck_hard.lua @@ -1,4 +1,4 @@ -return{ +return { hook_drop=function(P) if P.lastPiece.row==0 then P:lose() diff --git a/parts/eventsets/checkAttack_100.lua b/parts/eventsets/checkAttack_100.lua index e112a2c7d..4c7c789a5 100644 --- a/parts/eventsets/checkAttack_100.lua +++ b/parts/eventsets/checkAttack_100.lua @@ -1,8 +1,8 @@ -return{ +return { mesDisp=function(P) setFont(45) - mStr(("%.1f"):format(P.stat.atk),63,190) - mStr(("%.2f"):format(P.stat.atk/P.stat.row),63,310) + GC.mStr(("%d"):format(P.stat.atk),63,190) + GC.mStr(("%.2f"):format(P.stat.atk/P.stat.row),63,310) mText(TEXTOBJ.atk,63,243) mText(TEXTOBJ.eff,63,363) end, diff --git a/parts/eventsets/checkClearBoard.lua b/parts/eventsets/checkClearBoard.lua index ea51a2c98..40e87d481 100644 --- a/parts/eventsets/checkClearBoard.lua +++ b/parts/eventsets/checkClearBoard.lua @@ -1,14 +1,14 @@ -return{ +return { hook_drop=function(P) if P.garbageBeneath==0 then local D=P.modeData D.finished=D.finished+1 - if FIELD[D.finished+1]then + if FIELD[D.finished+1] then P.waiting=26 for i=#P.field,1,-1 do P.field[i],P.visTime[i]=nil end - setField(P,D.finished+1) + setField(P,FIELD[D.finished+1]) SYSFX.newShade(1.4,P.absFieldX,P.absFieldY,300*P.size,610*P.size,.6,.8,.6) SFX.play('warn_1') else diff --git a/parts/eventsets/checkLine_10.lua b/parts/eventsets/checkLine_10.lua index 02edb80a0..06dc16757 100644 --- a/parts/eventsets/checkLine_10.lua +++ b/parts/eventsets/checkLine_10.lua @@ -1,9 +1,9 @@ -return{ +return { mesDisp=function(P) setFont(55) local r=10-P.stat.row if r<0 then r=0 end - mStr(r,63,265) + GC.mStr(r,63,265) PLY.draw.drawTargetLine(P,r) end, hook_drop=function(P) diff --git a/parts/eventsets/checkLine_100.lua b/parts/eventsets/checkLine_100.lua index 2c6bef5cc..61e4e63bf 100644 --- a/parts/eventsets/checkLine_100.lua +++ b/parts/eventsets/checkLine_100.lua @@ -1,9 +1,9 @@ -return{ +return { mesDisp=function(P) setFont(55) local r=100-P.stat.row if r<0 then r=0 end - mStr(r,63,265) + GC.mStr(r,63,265) PLY.draw.drawTargetLine(P,r) end, hook_drop=function(P) diff --git a/parts/eventsets/checkLine_1000.lua b/parts/eventsets/checkLine_1000.lua index c1321251b..8a515f783 100644 --- a/parts/eventsets/checkLine_1000.lua +++ b/parts/eventsets/checkLine_1000.lua @@ -1,9 +1,9 @@ -return{ +return { mesDisp=function(P) setFont(55) local r=1000-P.stat.row if r<0 then r=0 end - mStr(r,63,265) + GC.mStr(r,63,265) PLY.draw.drawTargetLine(P,r) end, hook_drop=function(P) diff --git a/parts/eventsets/checkLine_20.lua b/parts/eventsets/checkLine_20.lua index d901ec8d2..a135bc40b 100644 --- a/parts/eventsets/checkLine_20.lua +++ b/parts/eventsets/checkLine_20.lua @@ -1,9 +1,9 @@ -return{ +return { mesDisp=function(P) setFont(55) local r=20-P.stat.row if r<0 then r=0 end - mStr(r,63,265) + GC.mStr(r,63,265) PLY.draw.drawTargetLine(P,r) end, hook_drop=function(P) diff --git a/parts/eventsets/checkLine_200.lua b/parts/eventsets/checkLine_200.lua index e75128970..8edb41c57 100644 --- a/parts/eventsets/checkLine_200.lua +++ b/parts/eventsets/checkLine_200.lua @@ -1,9 +1,9 @@ -return{ +return { mesDisp=function(P) setFont(55) local r=200-P.stat.row if r<0 then r=0 end - mStr(r,63,265) + GC.mStr(r,63,265) PLY.draw.drawTargetLine(P,r) end, hook_drop=function(P) diff --git a/parts/eventsets/checkLine_40.lua b/parts/eventsets/checkLine_40.lua index bd8f2956a..a5a62e4bc 100644 --- a/parts/eventsets/checkLine_40.lua +++ b/parts/eventsets/checkLine_40.lua @@ -1,9 +1,9 @@ -return{ +return { mesDisp=function(P) setFont(55) local r=40-P.stat.row if r<0 then r=0 end - mStr(r,63,265) + GC.mStr(r,63,265) PLY.draw.drawTargetLine(P,r) end, hook_drop=function(P) diff --git a/parts/eventsets/checkLine_400.lua b/parts/eventsets/checkLine_400.lua index 3457f3c56..852c69b92 100644 --- a/parts/eventsets/checkLine_400.lua +++ b/parts/eventsets/checkLine_400.lua @@ -1,9 +1,9 @@ -return{ +return { mesDisp=function(P) setFont(55) local r=400-P.stat.row if r<0 then r=0 end - mStr(r,63,265) + GC.mStr(r,63,265) PLY.draw.drawTargetLine(P,r) end, hook_drop=function(P) diff --git a/parts/eventsets/checkTurn_1.lua b/parts/eventsets/checkTurn_1.lua index c0ca5c3f0..8f96769b6 100644 --- a/parts/eventsets/checkTurn_1.lua +++ b/parts/eventsets/checkTurn_1.lua @@ -1,4 +1,4 @@ -return{ +return { hook_drop=function(P) if #PLY_ALIVE>1 then P.control=false @@ -13,7 +13,7 @@ return{ end end for i=1,#PLY_ALIVE do - if PLY_ALIVE[i].sid==(minMaxID==1e99 and minID or minMaxID)then + if PLY_ALIVE[i].sid==(minMaxID==1e99 and minID or minMaxID) then PLY_ALIVE[i].control=true return end diff --git a/parts/eventsets/checkTurn_7.lua b/parts/eventsets/checkTurn_7.lua index bf0ce2d75..d44d326fd 100644 --- a/parts/eventsets/checkTurn_7.lua +++ b/parts/eventsets/checkTurn_7.lua @@ -1,4 +1,4 @@ -return{ +return { hook_drop=function(P) if P.stat.piece%7==0 and #PLY_ALIVE>1 then P.control=false @@ -13,7 +13,7 @@ return{ end end for i=1,#PLY_ALIVE do - if PLY_ALIVE[i].sid==(minMaxID==1e99 and minID or minMaxID)then + if PLY_ALIVE[i].sid==(minMaxID==1e99 and minID or minMaxID) then PLY_ALIVE[i].control=true return end diff --git a/parts/eventsets/classic_e.lua b/parts/eventsets/classic_e.lua index 5f74a2c49..7913e8438 100644 --- a/parts/eventsets/classic_e.lua +++ b/parts/eventsets/classic_e.lua @@ -7,27 +7,26 @@ end local function GetGravity(lvl) lvl=lvl%256 return - lvl==0 and 48 or - lvl==1 and 43 or - lvl==2 and 38 or - lvl==3 and 33 or - lvl==4 and 28 or - lvl==5 and 23 or - lvl==6 and 18 or - lvl==7 and 13 or - lvl==8 and 8 or - lvl==9 and 6 or - lvl<13 and 5 or - lvl<16 and 4 or - lvl<19 and 3 or - lvl<29 and 2 or - 1 + lvl==0 and 48 or + lvl==1 and 43 or + lvl==2 and 38 or + lvl==3 and 33 or + lvl==4 and 28 or + lvl==5 and 23 or + lvl==6 and 18 or + lvl==7 and 13 or + lvl==8 and 8 or + lvl==9 and 6 or + lvl<13 and 5 or + lvl<16 and 4 or + lvl<19 and 3 or + lvl<29 and 2 or + 1 end -local gc_setColor=love.graphics.setColor -return{ +return { das=16,arr=6, sddas=6,sdarr=6, - irs=false,ims=false, + logicalIRS=false,logicalIMS=false, drop=6,lock=6, wait=10,fall=25, freshLimit=0, @@ -40,18 +39,18 @@ return{ keyCancel={5,6}, mesDisp=function(P) setFont(75) - mStr(GetLevelStr(P.modeData.lvl),63,210) + GC.mStr(GetLevelStr(P.modeData.lvl),63,210) mText(TEXTOBJ.speedLV,63,290) PLY.draw.drawProgress(P.stat.row,P.modeData.target) if P.modeData.drought>7 then if P.modeData.drought<=14 then - gc_setColor(1,1,1,P.modeData.drought/7-1) + GC.setColor(1,1,1,P.modeData.drought/7-1) else local gb=P.modeData.drought<=21 and 2-P.modeData.drought/14 or .5 - gc_setColor(1,gb,gb) + GC.setColor(1,gb,gb) end setFont(50) - mStr(P.modeData.drought,63,130) + GC.mStr(P.modeData.drought,63,130) mDraw(MODES.drought_l.icon,63,200,nil,.5) end end, diff --git a/parts/eventsets/classic_h.lua b/parts/eventsets/classic_h.lua index ece855a7f..83fb3196c 100644 --- a/parts/eventsets/classic_h.lua +++ b/parts/eventsets/classic_h.lua @@ -7,27 +7,26 @@ end local function GetGravity(lvl) lvl=lvl%256 return - lvl==0 and 48 or - lvl==1 and 43 or - lvl==2 and 38 or - lvl==3 and 33 or - lvl==4 and 28 or - lvl==5 and 23 or - lvl==6 and 18 or - lvl==7 and 13 or - lvl==8 and 8 or - lvl==9 and 6 or - lvl<13 and 5 or - lvl<16 and 4 or - lvl<19 and 3 or - lvl<29 and 2 or - 1 + lvl==0 and 48 or + lvl==1 and 43 or + lvl==2 and 38 or + lvl==3 and 33 or + lvl==4 and 28 or + lvl==5 and 23 or + lvl==6 and 18 or + lvl==7 and 13 or + lvl==8 and 8 or + lvl==9 and 6 or + lvl<13 and 5 or + lvl<16 and 4 or + lvl<19 and 3 or + lvl<29 and 2 or + 1 end -local gc_setColor=love.graphics.setColor -return{ +return { das=16,arr=6, sddas=3,sdarr=3, - irs=false,ims=false, + logicalIRS=false,logicalIMS=false, drop=3,lock=3, wait=10,fall=25, freshLimit=0, @@ -40,18 +39,18 @@ return{ keyCancel={5,6}, mesDisp=function(P) setFont(75) - mStr(GetLevelStr(P.modeData.lvl),63,210) + GC.mStr(GetLevelStr(P.modeData.lvl),63,210) mText(TEXTOBJ.speedLV,63,290) PLY.draw.drawProgress(P.stat.row,P.modeData.target) if P.modeData.drought>7 then if P.modeData.drought<=14 then - gc_setColor(1,1,1,P.modeData.drought/7-1) + GC.setColor(1,1,1,P.modeData.drought/7-1) else local gb=P.modeData.drought<=21 and 2-P.modeData.drought/14 or .5 - gc_setColor(1,gb,gb) + GC.setColor(1,gb,gb) end setFont(50) - mStr(P.modeData.drought,63,130) + GC.mStr(P.modeData.drought,63,130) mDraw(MODES.drought_l.icon,63,200,nil,.5) end end, diff --git a/parts/eventsets/classic_l.lua b/parts/eventsets/classic_l.lua index 885405ab8..893972bd7 100644 --- a/parts/eventsets/classic_l.lua +++ b/parts/eventsets/classic_l.lua @@ -7,27 +7,26 @@ end local function GetGravity(lvl) lvl=lvl%256 return - lvl==0 and 48 or - lvl==1 and 43 or - lvl==2 and 38 or - lvl==3 and 33 or - lvl==4 and 28 or - lvl==5 and 23 or - lvl==6 and 18 or - lvl==7 and 13 or - lvl==8 and 8 or - lvl==9 and 6 or - lvl<13 and 5 or - lvl<16 and 4 or - lvl<19 and 3 or - lvl<29 and 2 or - 1 + lvl==0 and 48 or + lvl==1 and 43 or + lvl==2 and 38 or + lvl==3 and 33 or + lvl==4 and 28 or + lvl==5 and 23 or + lvl==6 and 18 or + lvl==7 and 13 or + lvl==8 and 8 or + lvl==9 and 6 or + lvl<13 and 5 or + lvl<16 and 4 or + lvl<19 and 3 or + lvl<29 and 2 or + 1 end -local gc_setColor=love.graphics.setColor -return{ +return { das=16,arr=6, sddas=2,sdarr=2, - irs=false,ims=false, + logicalIRS=false,logicalIMS=false, drop=2,lock=2, wait=10,fall=25, freshLimit=0, @@ -40,18 +39,18 @@ return{ keyCancel={5,6}, mesDisp=function(P) setFont(75) - mStr(GetLevelStr(P.modeData.lvl),63,210) + GC.mStr(GetLevelStr(P.modeData.lvl),63,210) mText(TEXTOBJ.speedLV,63,290) PLY.draw.drawProgress(P.stat.row,P.modeData.target) if P.modeData.drought>7 then if P.modeData.drought<=14 then - gc_setColor(1,1,1,P.modeData.drought/7-1) + GC.setColor(1,1,1,P.modeData.drought/7-1) else local gb=P.modeData.drought<=21 and 2-P.modeData.drought/14 or .5 - gc_setColor(1,gb,gb) + GC.setColor(1,gb,gb) end setFont(50) - mStr(P.modeData.drought,63,130) + GC.mStr(P.modeData.drought,63,130) mDraw(MODES.drought_l.icon,63,200,nil,.5) end end, @@ -63,9 +62,9 @@ return{ local D=P.modeData D.drought=P.lastPiece.id==7 and 0 or D.drought+1 if P.stat.row>=D.target then - --if D.target>=200 then + if D.target>=100 then D.lvl=D.lvl+1 - --end + end local dropSpd=GetGravity(D.lvl) if dropSpd~=P.gameEnv.drop then P.gameEnv.drop,P.gameEnv.lock=dropSpd,dropSpd diff --git a/parts/eventsets/classic_u.lua b/parts/eventsets/classic_u.lua index 205da78ba..72cfc184d 100644 --- a/parts/eventsets/classic_u.lua +++ b/parts/eventsets/classic_u.lua @@ -4,30 +4,10 @@ local function GetLevelStr(lvl) lvl=lvl%256 return list[lvl] end -local function GetGravity(lvl) - lvl=lvl%256 - return - lvl==0 and 48 or - lvl==1 and 43 or - lvl==2 and 38 or - lvl==3 and 33 or - lvl==4 and 28 or - lvl==5 and 23 or - lvl==6 and 18 or - lvl==7 and 13 or - lvl==8 and 8 or - lvl==9 and 6 or - lvl<13 and 5 or - lvl<16 and 4 or - lvl<19 and 3 or - lvl<29 and 2 or - 1 -end -local gc_setColor=love.graphics.setColor -return{ +return { das=16,arr=6, sddas=1,sdarr=1, - irs=false,ims=false, + logicalIRS=false,logicalIMS=false, drop=1,lock=1, wait=10,fall=25, freshLimit=0, @@ -40,18 +20,18 @@ return{ keyCancel={5,6}, mesDisp=function(P) setFont(75) - mStr(GetLevelStr(P.modeData.lvl),63,210) + GC.mStr(GetLevelStr(P.modeData.lvl),63,210) mText(TEXTOBJ.speedLV,63,290) PLY.draw.drawProgress(P.stat.row,P.modeData.target) if P.modeData.drought>7 then if P.modeData.drought<=14 then - gc_setColor(1,1,1,P.modeData.drought/7-1) + GC.setColor(1,1,1,P.modeData.drought/7-1) else local gb=P.modeData.drought<=21 and 2-P.modeData.drought/14 or .5 - gc_setColor(1,gb,gb) + GC.setColor(1,gb,gb) end setFont(50) - mStr(P.modeData.drought,63,130) + GC.mStr(P.modeData.drought,63,130) mDraw(MODES.drought_l.icon,63,200,nil,.5) end end, diff --git a/parts/eventsets/construct_checker.lua b/parts/eventsets/construct_checker.lua new file mode 100644 index 000000000..8c460a7a2 --- /dev/null +++ b/parts/eventsets/construct_checker.lua @@ -0,0 +1,114 @@ +local gc_setColor,gc_draw=love.graphics.setColor,love.graphics.draw +local ply_applyField=PLY.draw.applyField + +local holePatterns={ + {-1,21,-1,21,-1,21,-1,21,-1,21}, + {21,-1,21,-1,21,-1,21,-1,21,-1} +} + +local targetField={} + +local function generateGuide(y,mirror) + local tfLength=#targetField + if tfLength>y then return end + mirror=mirror and 1 or 0 + for i=tfLength,y do + table.insert(targetField,TABLE.shift( + holePatterns[(i+mirror)%2+1] + )) + end +end + +local function calculateRankPts(P) + local points=1 + for y=1,#P.field do + local flag + for x=1,10 do + -- X guide is filled + if P.field[y][x]>0 and targetField[y][x]<0 then flag=true break end + -- Block guide is empty + if P.field[y][x]==0 and targetField[y][x]>0 then flag=true break end + end + if flag then break end + points=points+1 + end + P.modeData.rankPts=points + P.modeData.maxRankPts=math.max(points,P.modeData.maxRankPts) +end + +return { + fkey1=function(P) P.modeData.showGuide=not P.modeData.showGuide end, + fkey2=function(P) + P.modeData.mirror=not P.modeData.mirror + TABLE.cut(targetField) + generateGuide(#P.field+10,P.modeData.mirror) + calculateRankPts(P) + end, + mesDisp=function(P) + local D=P.modeData + mText(TEXTOBJ.grade,63,190) + mText(TEXTOBJ.line,63,310) + setFont(55) + GC.mStr(getConstructGrade(D.rankPts),63,125) + GC.mStr(D.rankPts-1,63,245) + + -- Display highest grade + if D.maxRankPts>D.rankPts then + gc_setColor(COLOR.lX) + setFont(20) + GC.mStr(text.highestGrade:repD(getConstructGrade(D.maxRankPts)),63,216) + GC.mStr(text.highestGrade:repD(D.maxRankPts-1),63,336) + end + + if not D.showGuide then return end + ply_applyField(P) + local mark=TEXTURE.puzzleMark + local firstMistake=nil + for y=1,D.rankPts+1 do + for x=1,10 do + local texture=targetField[y][x] + -- Missing blocks + if not P:solid(x,y) and texture>0 then + -- Missing block under overhang + if P:solid(x,y+1) then + firstMistake=firstMistake or y + gc_setColor(COLOR.R) + else + gc_setColor(COLOR.Z) + end + gc_draw(mark[texture],30*x-30,600-30*y) + elseif texture<0 then + -- X always gets displayed, color changes based on whether there is a block there + if P:solid(x,y) then + gc_setColor(COLOR.R) + firstMistake=firstMistake or y + elseif D.rankPts>y then + gc_setColor(COLOR.G) + else + gc_setColor(COLOR.Z) + end + gc_draw(mark[texture],30*x-30,600-30*y) + end + end + if y==firstMistake then + gc_setColor(1,0,0,.2*(math.sin(2*TIME())+1)) + love.graphics.rectangle("fill",0,600-30*y,300,30) + end + end + PLY.draw.cancelField() + end, + task=function(P) + local D=P.modeData + D.rankPts=1 + D.maxRankPts=1 + D.showGuide=true + D.mirror=false + TABLE.cut(targetField) + generateGuide(10) + end, + hook_drop=function(P) + local D=P.modeData + calculateRankPts(P) + generateGuide(#P.field+10) + end +} diff --git a/parts/eventsets/construct_invsg.lua b/parts/eventsets/construct_invsg.lua new file mode 100644 index 000000000..a1afc50df --- /dev/null +++ b/parts/eventsets/construct_invsg.lua @@ -0,0 +1,118 @@ +local gc_setColor,gc_draw=love.graphics.setColor,love.graphics.draw +local ply_applyField=PLY.draw.applyField + +local targetField={} + +local function getOpenHole(y,mirror) + if mirror then y=y+9 end + return -math.abs(((y-1) % 18)-9)+10 +end +local function generateGuide(y,mirror) + local l=#targetField + if l>y then + return + end + for i=l,y do + targetField[i] = {} + local h=getOpenHole(i,mirror) + for j=1,10 do + targetField[i][j]=h==j and 21 or -1 + end + end +end +local function calculateRankPts(P) + local points=1 + for y=1,#P.field do + local holePos=getOpenHole(y,P.modeData.mirror) + local flag + for x=1,10 do + if P.field[y][x]>0 and holePos~=x then flag=true break end + if P.field[y][x]==0 and holePos==x then flag=true break end + end + if flag then break end + if P:solid(holePos,y+1) then break end + points=points+1 + end + P.modeData.rankPts=points + P.modeData.maxRankPts=math.max(points,P.modeData.maxRankPts) +end + +return { + fkey1=function(P) P.modeData.showGuide=not P.modeData.showGuide end, + fkey2=function(P) + P.modeData.mirror=not P.modeData.mirror + TABLE.cut(targetField) + generateGuide(#P.field+10,P.modeData.mirror) + calculateRankPts(P) + end, + mesDisp=function(P) + local D=P.modeData + mText(TEXTOBJ.grade,63,190) + mText(TEXTOBJ.line,63,310) + setFont(55) + GC.mStr(getConstructGrade(D.rankPts),63,125) + GC.mStr(D.rankPts-1,63,245) + + -- Display highest grade + if D.maxRankPts>D.rankPts then + gc_setColor(COLOR.lX) + setFont(20) + GC.mStr(text.highestGrade:repD(getConstructGrade(D.maxRankPts)),63,216) + GC.mStr(text.highestGrade:repD(D.maxRankPts-1),63,336) + end + + if not D.showGuide then return end + ply_applyField(P) + local mark=TEXTURE.puzzleMark + local firstMistake=nil + for y=1,D.rankPts+1 do + for x=1,10 do + local texture=targetField[y][x] + -- Missing blocks + if not P:solid(x,y) and texture>0 then + -- Missing block under overhang + if P:solid(x,y+1) then + firstMistake=firstMistake or y + gc_setColor(COLOR.R) + else + gc_setColor(COLOR.Z) + end + gc_draw(mark[texture],30*x-30,600-30*y) + elseif texture<0 then + -- X always gets displayed, color changes based on whether there is a block there + if P:solid(x,y) then + gc_setColor(COLOR.R) + firstMistake=firstMistake or y + elseif D.rankPts>y then + gc_setColor(COLOR.G) + else + gc_setColor(COLOR.Z) + end + gc_draw(mark[texture],30*x-30,600-30*y) + end + end + if y==firstMistake then + gc_setColor(1,0,0,.2*(math.sin(2*TIME())+1)) + love.graphics.rectangle("fill",0,600-30*y,300,30) + end + end + PLY.draw.cancelField() + end, + task=function(P) + local D=P.modeData + D.rankPts=1 + D.showGuide=true + D.maxRankPts=1 + D.mirror=false + TABLE.cut(targetField) + generateGuide(10,D.mirror) + end, + hook_drop=function(P) + local oldPts=P.modeData.rankPts + calculateRankPts(P) + if oldPts>P.modeData.rankPts+2 then + P:_showText("REGRET!!",0,-120,80,'beat',.8) + end + generateGuide(#P.field+10,P.modeData.mirror) + end +} diff --git a/parts/eventsets/construct_sg.lua b/parts/eventsets/construct_sg.lua new file mode 100644 index 000000000..b6f6d4f7b --- /dev/null +++ b/parts/eventsets/construct_sg.lua @@ -0,0 +1,114 @@ +local gc_setColor,gc_draw=love.graphics.setColor,love.graphics.draw +local ply_applyField=PLY.draw.applyField + +local targetField={} + +local function getOpenHole(y,mirror) + if mirror then y=y+9 end + return -math.abs(((y-1) % 18)-9)+10 +end +local function generateGuide(y,mirror) + local l=#targetField + if l>y then + return + end + for i=l,y do + targetField[i] = {} + local h=getOpenHole(i,mirror) + for j=1,10 do + targetField[i][j]=h==j and -1 or 21 + end + end +end +local function calculateRankPts(P) + local points=1 + for y=1,#P.field do + local holePos=getOpenHole(y,P.modeData.mirror) + local flag + for x=1,10 do + if P.field[y][x]>0 and holePos==x then flag=true break end + if P.field[y][x]==0 and holePos~=x then flag=true break end + end + if flag then break end + if not P:solid(holePos,y+1) then break end + points=points+1 + end + P.modeData.rankPts=points + P.modeData.maxRankPts=math.max(points,P.modeData.maxRankPts) +end + +return { + fkey1=function(P) P.modeData.showGuide=not P.modeData.showGuide end, + fkey2=function(P) + P.modeData.mirror=not P.modeData.mirror + TABLE.cut(targetField) + generateGuide(#P.field+10,P.modeData.mirror) + calculateRankPts(P) + end, + mesDisp=function(P) + local D=P.modeData + mText(TEXTOBJ.grade,63,190) + mText(TEXTOBJ.line,63,310) + setFont(55) + GC.mStr(getConstructGrade(D.rankPts),63,125) + GC.mStr(D.rankPts-1,63,245) + + -- Display highest grade + if D.maxRankPts>D.rankPts then + gc_setColor(COLOR.lX) + setFont(20) + GC.mStr(text.highestGrade:repD(getConstructGrade(D.maxRankPts)),63,216) + GC.mStr(text.highestGrade:repD(D.maxRankPts-1),63,336) + end + + if not D.showGuide then return end + ply_applyField(P) + local mark=TEXTURE.puzzleMark + local firstMistake=nil + for y=1,D.rankPts+1 do + for x=1,10 do + local texture=targetField[y][x] + -- Missing blocks + if not P:solid(x,y) and texture>0 then + -- Missing block under overhang + if P:solid(x,y+1) then + firstMistake=firstMistake or y + gc_setColor(COLOR.R) + else + gc_setColor(COLOR.Z) + end + gc_draw(mark[texture],30*x-30,600-30*y) + elseif texture<0 then + -- X always gets displayed, color changes based on whether there is a block there + if P:solid(x,y) then + gc_setColor(COLOR.R) + firstMistake=firstMistake or y + elseif D.rankPts>y then + gc_setColor(COLOR.G) + else + gc_setColor(COLOR.Z) + end + gc_draw(mark[texture],30*x-30,600-30*y) + end + end + if y==firstMistake then + gc_setColor(1,0,0,.2*(math.sin(2*TIME())+1)) + love.graphics.rectangle("fill",0,600-30*y,300,30) + end + end + PLY.draw.cancelField() + end, + task=function(P) + local D=P.modeData + D.rankPts=1 + D.showGuide=true + D.maxRankPts=1 + D.mirror=false + TABLE.cut(targetField) + generateGuide(10,D.mirror) + end, + hook_drop=function(P) + calculateRankPts(P) + generateGuide(#P.field+10,P.modeData.mirror) + end +} diff --git a/parts/eventsets/defender_l.lua b/parts/eventsets/defender_l.lua index a27b537de..061f00e2b 100644 --- a/parts/eventsets/defender_l.lua +++ b/parts/eventsets/defender_l.lua @@ -1,46 +1,48 @@ -return{ +return { drop=5,lock=60, fall=6, mesDisp=function(P) setFont(55) - mStr(P.modeData.wave,63,200) - mStr(P.modeData.rpm,63,320) + GC.mStr(P.modeData.wave,63,200) + GC.mStr(P.modeData.rpm,63,320) mText(TEXTOBJ.wave,63,260) mText(TEXTOBJ.rpm,63,380) end, task=function(P) while true do - YIELD() + coroutine.yield() if P.control then local D=P.modeData - D.counter=D.counter+1 - local t= - D.wave<=60 and 240-2*D.wave or - D.wave<=120 and 120-(D.wave-60)or - D.wave<=180 and math.floor(60-(D.wave-120)*.5)or - 30 - if D.counter>=t then - D.counter=0 - for _=1,4 do - table.insert(P.atkBuffer,{line=generateLine(P.holeRND:random(10)),amount=1,countdown=5*t,cd0=5*t,time=0,sent=false,lv=2}) + D.timer=D.timer+1 + if P.atkBufferSum<30 then + local t= + D.wave<=60 and 240-2*D.wave or + D.wave<=120 and 120-(D.wave-60) or + D.wave<=180 and math.floor(60-(D.wave-120)*.5) or + 30 + if D.timer>=t then + D.timer=0 + for _=1,4 do + table.insert(P.atkBuffer,{line=generateLine(P.holeRND:random(10)),amount=1,countdown=5*t,cd0=5*t,time=0,sent=false,lv=2}) + end + P.atkBufferSum=P.atkBufferSum+4 + P.stat.recv=P.stat.recv+4 + D.wave=D.wave+1 + D.rpm=math.floor(144e3/t)*.1 + if D.wave==60 then + P:_showText(text.great,0,-140,100,'appear',.6) + P.gameEnv.pushSpeed=3 + P.dropDelay,P.gameEnv.drop=4,4 + elseif D.wave==120 then + P:_showText(text.awesome,0,-140,100,'appear',.6) + P.gameEnv.pushSpeed=4 + P.dropDelay,P.gameEnv.drop=3,3 + elseif D.wave==180 then + P:_showText(text.maxspeed,0,-140,100,'appear',.6) + P.dropDelay,P.gameEnv.drop=2,2 + end + P:shakeField(3) end - P.atkBufferSum=P.atkBufferSum+4 - P.stat.recv=P.stat.recv+4 - D.wave=D.wave+1 - D.rpm=math.floor(144e3/t)*.1 - if D.wave==60 then - P:_showText(text.great,0,-140,100,'appear',.6) - P.gameEnv.pushSpeed=3 - P.dropDelay,P.gameEnv.drop=4,4 - elseif D.wave==120 then - P:_showText(text.awesome,0,-140,100,'appear',.6) - P.gameEnv.pushSpeed=4 - P.dropDelay,P.gameEnv.drop=3,3 - elseif D.wave==180 then - P:_showText(text.maxspeed,0,-140,100,'appear',.6) - P.dropDelay,P.gameEnv.drop=2,2 - end - P:shakeField(3) end end end diff --git a/parts/eventsets/defender_n.lua b/parts/eventsets/defender_n.lua index 7d89786e9..2702f65ea 100644 --- a/parts/eventsets/defender_n.lua +++ b/parts/eventsets/defender_n.lua @@ -1,46 +1,48 @@ -return{ +return { drop=30,lock=60, fall=10, mesDisp=function(P) setFont(55) - mStr(P.modeData.wave,63,200) - mStr(P.modeData.rpm,63,320) + GC.mStr(P.modeData.wave,63,200) + GC.mStr(P.modeData.rpm,63,320) mText(TEXTOBJ.wave,63,260) mText(TEXTOBJ.rpm,63,380) end, task=function(P) while true do - YIELD() + coroutine.yield() if P.control then local D=P.modeData - D.counter=D.counter+1 - local t= - D.wave<=60 and 360-D.wave*3 or - D.wave<=120 and 180-(D.wave-60)*2 or - D.wave<=180 and 120-(D.wave-120)or - 60 - if D.counter>=t then - D.counter=0 - for _=1,3 do - table.insert(P.atkBuffer,{line=generateLine(P.holeRND:random(10)),amount=1,countdown=2*t,cd0=2*t,time=0,sent=false,lv=1}) + D.timer=D.timer+1 + if P.atkBufferSum<20 then + local t= + D.wave<=40 and 360-D.wave*4 or + D.wave<=80 and 200-(D.wave-40)*2 or + D.wave<=140 and 120-(D.wave-80) or + 60 + if D.timer>=t then + D.timer=0 + for _=1,3 do + table.insert(P.atkBuffer,{line=generateLine(P.holeRND:random(10)),amount=1,countdown=2*t,cd0=2*t,time=0,sent=false,lv=1}) + end + P.atkBufferSum=P.atkBufferSum+3 + P.stat.recv=P.stat.recv+3 + D.wave=D.wave+1 + D.rpm=math.floor(108e3/t)*.1 + if D.wave==60 then + P:_showText(text.great,0,-140,100,'appear',.6) + P.gameEnv.pushSpeed=2 + P.dropDelay,P.gameEnv.drop=20,20 + elseif D.wave==120 then + P:_showText(text.awesome,0,-140,100,'appear',.6) + P.gameEnv.pushSpeed=3 + P.dropDelay,P.gameEnv.drop=10,10 + elseif D.wave==140 then + P.dropDelay,P.gameEnv.drop=5,5 + P:_showText(text.maxspeed,0,-140,100,'appear',.6) + end + P:shakeField(3) end - P.atkBufferSum=P.atkBufferSum+3 - P.stat.recv=P.stat.recv+3 - D.wave=D.wave+1 - D.rpm=math.floor(108e3/t)*.1 - if D.wave==60 then - P:_showText(text.great,0,-140,100,'appear',.6) - P.gameEnv.pushSpeed=2 - P.dropDelay,P.gameEnv.drop=20,20 - elseif D.wave==120 then - P:_showText(text.awesome,0,-140,100,'appear',.6) - P.gameEnv.pushSpeed=3 - P.dropDelay,P.gameEnv.drop=10,10 - elseif D.wave==180 then - P.dropDelay,P.gameEnv.drop=5,5 - P:_showText(text.maxspeed,0,-140,100,'appear',.6) - end - P:shakeField(3) end end end diff --git a/parts/eventsets/dig_100l.lua b/parts/eventsets/dig_100l.lua index 5c2299ac4..9ec7f3b71 100644 --- a/parts/eventsets/dig_100l.lua +++ b/parts/eventsets/dig_100l.lua @@ -1,7 +1,7 @@ -return{ +return { mesDisp=function(P) setFont(55) - mStr(100-P.stat.dig,63,265) + GC.mStr(100-P.stat.dig,63,265) end, hook_drop=function(P) for _=1,math.min(10,100-P.stat.dig)-P.garbageBeneath do diff --git a/parts/eventsets/dig_10l.lua b/parts/eventsets/dig_10l.lua index eccb98545..c5aed4d88 100644 --- a/parts/eventsets/dig_10l.lua +++ b/parts/eventsets/dig_10l.lua @@ -1,7 +1,7 @@ -return{ +return { mesDisp=function(P) setFont(55) - mStr(10-P.stat.dig,63,265) + GC.mStr(10-P.stat.dig,63,265) end, hook_drop=function(P) if P.stat.dig==10 then diff --git a/parts/eventsets/dig_400l.lua b/parts/eventsets/dig_400l.lua index 1a56310df..23eb1cbc8 100644 --- a/parts/eventsets/dig_400l.lua +++ b/parts/eventsets/dig_400l.lua @@ -1,7 +1,7 @@ -return{ +return { mesDisp=function(P) setFont(55) - mStr(400-P.stat.dig,63,265) + GC.mStr(400-P.stat.dig,63,265) end, hook_drop=function(P) for _=1,math.min(10,400-P.stat.dig)-P.garbageBeneath do diff --git a/parts/eventsets/dig_40l.lua b/parts/eventsets/dig_40l.lua index 11c240408..8233b7575 100644 --- a/parts/eventsets/dig_40l.lua +++ b/parts/eventsets/dig_40l.lua @@ -1,7 +1,7 @@ -return{ +return { mesDisp=function(P) setFont(55) - mStr(40-P.stat.dig,63,265) + GC.mStr(40-P.stat.dig,63,265) end, hook_drop=function(P) for _=1,math.min(10,40-P.stat.dig)-P.garbageBeneath do diff --git a/parts/eventsets/dig_h.lua b/parts/eventsets/dig_h.lua index e040b141a..45cc0f674 100644 --- a/parts/eventsets/dig_h.lua +++ b/parts/eventsets/dig_h.lua @@ -1,16 +1,16 @@ -return{ +return { mesDisp=function(P) setFont(60) - mStr(P.modeData.wave,63,310) + GC.mStr(P.modeData.wave,63,310) mText(TEXTOBJ.wave,63,375) end, task=function(P) while true do - YIELD() + coroutine.yield() if P.control then local D=P.modeData D.timer=D.timer+1 - if D.timer>=math.max(90,180-D.wave)then + if D.timer>=math.max(90,180-D.wave) then P:garbageRise(21,1,P:getHolePos()) P.stat.recv=P.stat.recv+1 D.timer=0 diff --git a/parts/eventsets/dig_u.lua b/parts/eventsets/dig_u.lua index 35bb49dbd..ac357a532 100644 --- a/parts/eventsets/dig_u.lua +++ b/parts/eventsets/dig_u.lua @@ -1,16 +1,16 @@ -return{ +return { mesDisp=function(P) setFont(60) - mStr(P.modeData.wave,63,310) + GC.mStr(P.modeData.wave,63,310) mText(TEXTOBJ.wave,63,375) end, task=function(P) while true do - YIELD() + coroutine.yield() if P.control then local D=P.modeData D.timer=D.timer+1 - if D.timer>=math.max(30,80-.3*D.wave)then + if D.timer>=math.max(30,80-.3*D.wave) then P:garbageRise(20+D.wave%5,1,P:getHolePos()) P.stat.recv=P.stat.recv+1 D.timer=0 diff --git a/parts/eventsets/infinite.lua b/parts/eventsets/infinite.lua new file mode 100644 index 000000000..339a7100c --- /dev/null +++ b/parts/eventsets/infinite.lua @@ -0,0 +1,35 @@ +return { + mesDisp=function(P) + setFont(45) + GC.mStr(("%d"):format(P.stat.atk),63,190) + GC.mStr(("%.2f"):format(P.stat.atk/P.stat.row),63,310) + mText(TEXTOBJ.atk,63,243) + mText(TEXTOBJ.eff,63,363) + + local opacity=math.max(.4,1-(P.stat.time-P.modeData.lastChange)) + if #P.field>15 then opacity=opacity*.7 end + setFont(25) + GC.setColor(1,1,1,opacity) + GC.mStr(P.modeData.infHeight and text.infHeightOn or text.infHeightOff,300,80) + setFont(20) + GC.mStr(text.infHeightHint,300,120) + end, + task=function(P) + P.modeData.infHeight=false + P.modeData.lastChange=0 + end, + fkey1=function(P) + P.modeData.infHeight=not P.modeData.infHeight + P.modeData.lastChange=P.stat.time + end, + hook_drop=function(P) + local heightTarget=P.field and #P.field+8 or 20 + local env=P.gameEnv + if P.modeData.infHeight then + env.fieldH=math.max(env.fieldH,heightTarget) + elseif env.fieldH~=20 then + -- if not infHeight, then only decrease height + env.fieldH=MATH.clamp(env.fieldH,heightTarget,20) + end + end +} diff --git a/parts/eventsets/marathon_bfmax.lua b/parts/eventsets/marathon_bfmax.lua index 078c5b3fa..b2e3d907c 100644 --- a/parts/eventsets/marathon_bfmax.lua +++ b/parts/eventsets/marathon_bfmax.lua @@ -1,6 +1,6 @@ -local dropSpeed={50,40,30,25,20,15,12,9,7,5,4,3,2,1,1,.5,.5,.25,.25} +local dropSpeed={60,50,40,30,25,20,15,12,9,7,5,4,3,2,1,1,.5,.5,.25,.25} -return{ +return { drop=60, wait=8, fall=20, @@ -14,11 +14,11 @@ return{ hook_drop=function(P) local flag local l=P.lastPiece - if P.combo>1 then flag=true;P:showText("2x",0,-220,40,'flicker',.3)end - if l.spin then flag=true;P:showText("spin",0,-180,40,'flicker',.3)end - if l.row>1 then flag=true;P:showText("1+",0,-140,40,'flicker',.3)end - if l.hpc then flag=true;P:showText("HPC",0,-100,40,'flicker',.3)end - if l.pc then P:showText("PC",0,-90,100,'beat',.5)end + if P.combo>1 then flag=true;P:showText("2x",0,-220,40,'flicker',.3) end + if l.spin then flag=true;P:showText("spin",0,-180,40,'flicker',.3) end + if l.row>1 then flag=true;P:showText("1+",0,-140,40,'flicker',.3) end + if l.hpc then flag=true;P:showText("HPC",0,-100,40,'flicker',.3) end + if l.pc then P:showText("PC",0,-90,100,'beat',.5) end if flag then P:lose() else diff --git a/parts/eventsets/marathon_inf.lua b/parts/eventsets/marathon_inf.lua index 80e8fa129..d9f88cfb1 100644 --- a/parts/eventsets/marathon_inf.lua +++ b/parts/eventsets/marathon_inf.lua @@ -2,7 +2,7 @@ local dropSpeed={ 50,42,35,30,25,20,16,13,11,10, 9,8,7,6,5,5,4,4,3,3, 3,2,2,2,2,1,1,1,1,1, - .5,.5,.5,.5,.25,.25,.25,.125,.125,--Total 39 numbers, switch to 20G when reach 400 lines + .5,.5,.5,.5,.25,.25,.25,.125,.125,-- Total 39 numbers, switch to 20G when reach 400 lines } local lockDelay={ 57,54,51,48,46,44,42,40,38,36, @@ -17,7 +17,7 @@ local lockDelay={ 4,4,4,4,4,4,4,4,4,4, 3,3,3,3,3,3,3,3,3,3, 2,2,2,2,2,2,2,2,2,2, - 1,1,1,1,1,1,1,1,1,--Finish at 1700 + 1,1,1,1,1,1,1,1,1,-- Finish at 1700 } return diff --git a/parts/eventsets/master_ex.lua b/parts/eventsets/master_ex.lua index 410c467a4..16f024571 100644 --- a/parts/eventsets/master_ex.lua +++ b/parts/eventsets/master_ex.lua @@ -1,4 +1,3 @@ -local gc=love.graphics local sectionName={"D","C","B","A","A+","S-","S","S+","S+","SS","SS","U","U","X","X+"} local passPoint=16 @@ -8,10 +7,10 @@ local function getRollGoal(P) if rem<=0 then return 0 end local goal=math.floor(rem)*4 rem=rem%1 - return goal + (rem>0.3 and 4 or rem*10) + return goal+(rem>0.3 and 4 or rem*10) end -return{ +return { drop=0,lock=15, wait=15,fall=6, noTele=true, @@ -24,27 +23,27 @@ return{ mesDisp=function(P) local h=(3600-P.stat.frame)/10 if h>0 then - gc.setColor(1,1,1,.12) - gc.rectangle('fill',0,475-h,125,h,4) - gc.setColor(COLOR.Z) + GC.setColor(1,1,1,.12) + GC.rectangle('fill',0,475-h,125,h,4) + GC.setColor(COLOR.Z) end mText(TEXTOBJ.line,63,310) mText(TEXTOBJ.techrash,63,420) mText(TEXTOBJ.grade,63,180) setFont(20) - mStr(("%.1f"):format(P.modeData.rankPoint/10),63,208) + GC.mStr(("%.1f"):format(P.modeData.rankPoint/10),63,208) setFont(55) - mStr(P.modeData.rankName,63,125) + GC.mStr(P.modeData.rankName,63,125) setFont(75) - mStr(P.stat.row,63,230) - mStr(P.stat.clears[4],63,340) + GC.mStr(P.stat.row,63,230) + GC.mStr(P.stat.clears[4],63,340) PLY.draw.drawTargetLine(P,getRollGoal(P)) end, hook_drop=function(P) - if P.modeData.rankPoint<140-passPoint then--If Less then X + if P.modeData.rankPoint<140-passPoint then-- If Less then X local R=#P.clearedRow if R>0 then - if R==4 then R=10 end--Techrash +10 + if R==4 then R=10 end-- Techrash +10 P.modeData.rankPoint=math.min(P.modeData.rankPoint+R,140-passPoint) P.modeData.rankName=sectionName[math.floor(P.modeData.rankPoint/10)+1] end @@ -54,7 +53,7 @@ return{ P.modeData.rankPoint=0 P.modeData.rankName=sectionName[1] while true do - YIELD() + coroutine.yield() if P.stat.frame>=3600 then P.modeData.rankPoint=math.min(P.modeData.rankPoint+passPoint,140) P.modeData.rankName=sectionName[math.floor(P.modeData.rankPoint/10)+1] diff --git a/parts/eventsets/master_final.lua b/parts/eventsets/master_final.lua index b6ad08a79..b8fdbbd6f 100644 --- a/parts/eventsets/master_final.lua +++ b/parts/eventsets/master_final.lua @@ -1,4 +1,4 @@ -return{ +return { drop=0,lock=12, wait=10,fall=10, noTele=true, @@ -21,18 +21,18 @@ return{ if D.pt%100==99 then SFX.play('warn_1') - elseif D.pt>=D.target then--Level up! - s=D.target/100--range from 1 to 9 + elseif D.pt>=D.target then-- Level up! + s=D.target/100-- range from 1 to 9 local E=P.gameEnv if s<4 then P:stageComplete(s) - --First 300 + -- First 300 if s~=1 then E.lock=E.lock-1 end if s~=2 then E.wait=E.wait-1 end if s~=3 then E.fall=E.fall-1 end D.target=D.target+100 elseif s<10 then - if s==5 then BGM.play('distortion')end + if s==5 then BGM.play('distortion') end P:stageComplete(s) if s==4 or s==7 then E.das=E.das-1 end if s%3==0 then E.lock=E.lock-1 diff --git a/parts/eventsets/master_g.lua b/parts/eventsets/master_g.lua index 9d09ce2e3..2fcc12b8d 100644 --- a/parts/eventsets/master_g.lua +++ b/parts/eventsets/master_g.lua @@ -1,126 +1,147 @@ -local gc=love.graphics -local regretDelay=-1 -local int_grade=0 -local grade_points=0 -local int_grade_boosts={0,1,2,3,4,5,5,6,6,7,7,7,8,8,8,9,9,9,10,11,12,12,12,13,13,14,14,15,15,16,16,17,17,18,18,19,19,20,20,21,21,22,22,23,23,24,24,25,25,26} -local coolList={false,false,false,false,false,false,false,false,false} -local regretList={false,false,false,false,false,false,false,false,false,false} -local gradeList={ - "9","8","7","6","5","4","3","2","1", - "S1","S2","S3","S4","S5","S6","S7","S8","S9", - "m1","m2","m3","m4","m5","m6","m7","m8","m9", - "M","MK","MV","MO","MM-","MM","MM+","GM-","GM","GM+","TM-","TM","TM+" -} -local spd_lvl=0 -local cools=0 -local regrets=0 -local prevSectTime=0 -local isInRoll=false -local rollGrades=0 +local _igb={0,1,2,3,4,5,5,6,6,7,7,7,8,8,8,9,9,9,10,11,12,12,12,13,13,14,14,15,15,16,16,17,17,18,18,19,19,20,20,21,21,22,22,23,23,24,24,25,25,26} +local function getInternalGradeBoosts(internal_grade) + return _igb[MATH.clamp(internal_grade+1,1,#_igb)] +end + +local decayRate={125,80,80,50,45,45,45,40,40,40,40,40,30,30,30,20,20,20,20,20,15,15,15,15,15,15,15,15,15,15,10,10,10,9,9,9,8,8,8,7,7,7,6} + local cool_time={3120,3120,2940,2700,2700,2520,2520,2280,2280,0} -local reg_time= {5400,4500,4500,4080,3600,3600,3000,3000,3000,3000} -local prevDrop70=false --determines if previous piece has level less than __70 -local nextSpeedUp=false --determines if the next section speed should be boosted by 100 -local isInRollTrans=false +local regret_time= {5400,4500,4500,4080,3600,3600,3000,3000,3000,3000} local function getGrav(l) return - l<30 and 64 or - l<35 and 43 or - l<40 and 32 or - l<50 and 26 or - l<60 and 21 or - l<70 and 16 or - l<80 and 8 or - l<90 and 6 or - l<120 and 4 or - l<160 and 3 or - l<200 and 2 or - l<220 and 64 or - l<230 and 8 or - l<233 and 4 or - l<236 and 3 or - l<243 and 2 or - l<300 and 1 or - l<360 and 0.5 or - l<450 and 0.25 or - 0 + l<30 and 64 or + l<35 and 43 or + l<40 and 32 or + l<50 and 26 or + l<60 and 21 or + l<70 and 16 or + l<80 and 8 or + l<90 and 6 or + l<120 and 4 or + l<160 and 3 or + l<200 and 2 or + l<220 and 64 or + l<230 and 8 or + l<233 and 4 or + l<236 and 3 or + l<243 and 2 or + l<300 and 1 or + l<360 and 0.5 or + l<450 and 0.25 or + 0 end local function getLock(l) return - l<900 and 30 or - l<1100 and 19 or - 15 + l<900 and 30 or + l<1100 and 19 or + 15 end local function getWait(l) return - l<700 and 23 or - l<800 and 16 or - l<1000 and 12 or - l<1100 and 7 or - 6 + l<700 and 23 or + l<800 and 16 or + l<1000 and 12 or + l<1100 and 7 or + 6 end local function getFall(l) return - l<500 and 25 or - l<600 and 18 or - l<700 and 12 or - l<800 and 8 or - 4 + l<500 and 25 or + l<600 and 18 or + l<700 and 12 or + l<800 and 8 or + 4 end local function getDas(l) return - l<500 and 10 or - l<900 and 8 or - 6 + l<500 and 10 or + l<900 and 8 or + 6 end -local function getGrade() - if int_grade==nil then int_grade=0 end - if rollGrades==nil then rollGrades=0 end - return gradeList[math.max(math.min(math.floor(int_grade_boosts[math.min(int_grade+1,#int_grade_boosts)]+rollGrades+cools+1-regrets),#gradeList),1)] +local function getCurrentGrade(D) + if not D.int_grade then D.int_grade=0 end + if not D.roll_grades then D.roll_grades=0 end + return math.floor(math.max(1,getInternalGradeBoosts(D.internal_grade)+D.rollGrades+D.cools+1-D.regrets)) end -local function addGrade(row, cmb, lvl) -- IGS = internal grade system +local function addGrade(D,row,cmb,lvl) -- IGS = internal grade system if row<1 then return end local pts=0 local cmb_mult=1.0 local lvl_mult=math.floor(lvl/250)+1 - if row==1 then - pts=int_grade<5 and 10 or int_grade<10 and 5 or 2 + pts=D.internal_grade<5 and 10 or D.internal_grade<10 and 5 or 2 cmb_mult=1.0 elseif row==2 then - pts=int_grade<3 and 20 or int_grade<6 and 15 or int_grade<10 and 10 or 12 + pts=D.internal_grade<3 and 20 or D.internal_grade<6 and 15 or D.internal_grade<10 and 10 or 12 cmb_mult=cmb==1 and 1 or cmb<4 and 1.2 or cmb<8 and 1.4 or cmb<10 and 1.5 or 2.0 elseif row==3 then - pts=int_grade==0 and 40 or int_grade<4 and 30 or int_grade<7 and 20 or int_grade<10 and 15 or 13 + pts=D.internal_grade==0 and 40 or D.internal_grade<4 and 30 or D.internal_grade<7 and 20 or D.internal_grade<10 and 15 or 13 cmb_mult=cmb==1 and 1 or cmb<10 and 1+(cmb+2)*0.1 or 2 else - pts=int_grade==0 and 50 or int_grade<5 and 40 or 30 + pts=D.internal_grade==0 and 50 or D.internal_grade<5 and 40 or 30 cmb_mult=cmb==1 and 1 or cmb==2 and 1.5 or cmb<6 and (0.2*cmb)+1.2 or cmb<10 and (0.1*cmb)+1.7 or 3 end - - grade_points=grade_points+(pts*cmb_mult*lvl_mult) - if grade_points>=100 then - grade_points=0 - int_grade=int_grade+1 + D.grade_points=D.grade_points+(pts*cmb_mult*lvl_mult) + if D.grade_points>=100 then + D.grade_points=0 + D.internal_grade=D.internal_grade+1 + D.decayTimer=0 end end -local function getRollGoal() +local function getRollGoal(D,isGreenLine) + local invis=D.cools>8 -- get amount of grades needed for TM+ - local rem=#gradeList-(int_grade_boosts[math.min(int_grade+1,#int_grade_boosts)]+rollGrades+cools+1-regrets) + local rem=40-getCurrentGrade(D)-( + -- adjust for clear bonus + isGreenLine and 0 or + invis and 1.6 or .5 + ) if rem<=0 then return 0 end local goal=0 - if cools>8 then + if invis then goal=math.floor(rem)*4 rem=rem%1 - return goal + (rem>0.3 and 4 or rem*10) + return goal+(rem>0.3 and 4 or rem*10) else - goal=math.floor(rem/0.26)*4 - rem=rem%0.26 - return goal + (rem>0.12 and 4 or rem*25) + goal=math.floor(rem/.26)*4 + rem=rem%.26 + return goal+(rem>0.12 and 4 or rem*25) + end +end +local function getCurSection(D) + return math.ceil((D.pt+1)/100) +end +local function getSectionState(P,section) + local D=P.modeData + local cool,miss,regret=false,false,false + if D.coolList[section] then cool=true end + if D.regretList[section] then regret=true end + + if section==getCurSection(D) and P.stat.frame-D.prevSectTime>cool_time[section] then + miss=true + end + return cool,miss,regret +end +local function setSectionColor(cool,regret,isCurSection) + if not (cool or regret) then + GC.setColor(0.6,0.6,0.6,isCurSection and 0.25 or 0.6) + else + GC.setColor(regret and 1 or 0, cool and 1 or 0, 0, 1) + end +end +local function setCurSectionColor(cool, miss, regret) + if cool and regret then + GC.setColor(COLOR.Y) + elseif cool then + GC.setColor(COLOR.G) + elseif regret then + GC.setColor(COLOR.R) + elseif miss then + GC.setColor(COLOR.lX) end end -return{ +return { drop=64, lock=30, wait=23, @@ -128,54 +149,49 @@ return{ keyCancel={10,11,12,14,15,16,17,18,19,20}, das=16,arr=1, minsdarr=1, - ihs=true,irs=true,ims=false, + logicalIRS=true,logicalIHS=true,logicalIMS=false, mesDisp=function(P) - gc.setColor(1,1,1,1) + local D=P.modeData + GC.setColor(1,1,1,1) setFont(45) mText(TEXTOBJ.grade,63,180) setFont(60) - mStr(getGrade(),63,110) -- draw grade + GC.mStr(getMasterGrade(getCurrentGrade(D)),63,110) -- draw grade for i=1,10 do -- draw cool/regret history - if not (coolList[i] or regretList[i]) then -- neither cool nor regret - gc.setColor(0.6,0.6,0.6,P.modeData.pt<(i-1)*100 and 0.25 or 0.6) - else - gc.setColor(regretList[i] and 1 or 0, coolList[i] and 1 or 0, 0, 1) - end - gc.circle('fill',-10,150+i*25,10) - gc.setColor(1,1,1,1) + setSectionColor(D.coolList[i],D.regretList[i],i==getCurSection(D)) + GC.circle('fill',-10,150+i*25,10) + GC.setColor(1,1,1,1) end - if isInRoll then + if D.isInRoll then setFont(20) - mStr(("%.1f"):format(rollGrades),63,208) -- draw roll grades - gc.setLineWidth(2) - gc.setColor(.98,.98,.98,.8) - gc.rectangle('line',0,240,126,80,4) - gc.setColor(.98,.98,.98,.4) - gc.rectangle('fill',0+2,240+2,126-4,80-4,2) -- draw time box - setFont(45) - local t=(P.stat.frame-prevSectTime)/60 + GC.mStr(("%.1f"):format(D.rollGrades),63,208) -- draw roll grades + GC.setLineWidth(2) + GC.setColor(.98,.98,.98,.8) + GC.rectangle('line',0,240,126,80,4) + GC.setColor(.98,.98,.98,.4) + GC.rectangle('fill',0+2,240+2,126-4,80-4,2) -- draw time box + + setFont(45) -- Draw time text + local t=(P.stat.frame-D.prevSectTime)/60 local T=("%.1f"):format(60-t) - gc.setColor(COLOR.dH) - mStr(T,65,250) -- draw time + GC.setColor(COLOR.dH) + GC.mStr(T,65,250) t=t/60 - gc.setColor(1.7*t,2.3-2*t,.3) - mStr(T,63,248) - PLY.draw.drawTargetLine(P,getRollGoal()) + GC.setColor(1.7*t,2.3-2*t,.3) + GC.mStr(T,63,248) + + GC.setColor(COLOR.O) + PLY.draw.drawTargetLine(P,getRollGoal(D),true) + + GC.setColor(COLOR.G) + PLY.draw.drawTargetLine(P,getRollGoal(D,true),true) else - -- draw level counter + -- Not in roll setFont(20) - mStr(grade_points,63,208) + GC.mStr(D.grade_points,63,208) setFont(45) - if coolList[math.ceil(P.modeData.pt/100+0.01)] then - gc.setColor(0,1,0,1) - elseif P.stat.frame-prevSectTime > cool_time[math.ceil(P.modeData.pt/100+0.01)] then - gc.setColor(0.7,0.7,0.7,1) - end - if coolList[math.ceil(P.modeData.pt/100+0.01)] and regretList[math.ceil(P.modeData.pt/100+0.01)] then - gc.setColor(1,1,0,1) - elseif regretList[math.ceil(P.modeData.pt/100+0.01)] then - gc.setColor(1,0,0,1) - end + + setCurSectionColor(getSectionState(P,getCurSection(D))) PLY.draw.drawProgress(P.modeData.pt,P.modeData.target) end end, @@ -184,59 +200,55 @@ return{ local c=#P.clearedRow - if cools>8 and isInRoll then - rollGrades=rollGrades+(c==4 and 1 or 0.1*c) + if D.cools>8 and D.isInRoll then -- invis roll grades + D.rollGrades=D.rollGrades+(c==4 and 1 or 0.1*c) return - elseif isInRoll then - rollGrades=rollGrades+(c==4 and 0.26 or 0.04*c) + elseif D.isInRoll then -- fading roll grades + D.rollGrades=D.rollGrades+(c==4 and 0.26 or 0.04*c) return end if c==0 and D.pt+1>=D.target then return end local s=c<3 and c+1 or c==3 and 5 or 7 if P.combo>7 then s=s+2 - elseif P.combo>3 then s=s+1 - end - - addGrade(c,P.combo,D.pt) - - D.pt=D.pt+s - spd_lvl=spd_lvl+1 + elseif P.combo>3 then s=s+1 end - P.gameEnv.drop=getGrav(spd_lvl) - - if(P.gameEnv.drop==0) then - P:set20G(true) + if not D.isInRoll then + addGrade(D,c,P.combo,D.pt) + D.pt=D.pt+s + D.speed_level=D.speed_level+s end - if D.pt%100>70 and not prevDrop70 then - if P.stat.frame-prevSectTime < cool_time[math.ceil(D.pt/100)] then - cools=cools+1 - coolList[math.ceil(D.pt/100)]=true + if D.pt%100>70 and not D.prevDrop70 then + if P.stat.frame-D.prevSectTime=D.target then--Level up! - spd_lvl=nextSpeedUp and spd_lvl+100 or spd_lvl - nextSpeedUp=false - prevDrop70=false + elseif D.pt>=D.target then-- Level up! + D.speed_level=D.nextSpeedUp and D.speed_level+100 or D.speed_level + D.nextSpeedUp=false + D.prevDrop70=false s=D.target/100 local E=P.gameEnv - E.lock=getLock(spd_lvl) - E.wait=getWait(spd_lvl) - E.fall=getFall(spd_lvl) - E.das=getDas(spd_lvl) + E.drop=getGrav(D.speed_level) + E.lock=getLock(D.speed_level) + E.wait=getWait(D.speed_level) + E.fall=getFall(D.speed_level) + E.das =getDas(D.speed_level) + if (E.drop==0) then P:set20G(true) end - if P.stat.frame-prevSectTime > reg_time[math.ceil(s)] then - regrets=regrets+1 - regretDelay=60 + if P.stat.frame-D.prevSectTime > regret_time[math.ceil(s)] then + D.regrets=D.regrets+1 + D.regretDelay=60 end - prevSectTime=P.stat.frame + D.prevSectTime=P.stat.frame if s==2 then BG.set('rainbow') elseif s==4 then @@ -244,7 +256,7 @@ return{ elseif s==5 then if P.stat.frame>420*60 then D.pt=500 - P:win('finish') + P:torikanEnd(420) return else BG.set('glow') @@ -253,7 +265,7 @@ return{ elseif s==6 then BG.set('lightning') elseif s>9 then - if cools>8 then + if D.cools>8 then if E.lockFX and E.lockFX>1 then E.lockFX=1 end P:setInvisible(5) else @@ -262,7 +274,7 @@ return{ D.pt=999 P.waiting=240 BGM.stop() - isInRollTrans=true + D.isInRollTrans=true return end D.target=D.target<900 and D.target+100 or 999 @@ -271,38 +283,37 @@ return{ end end, task=function(P) - regretDelay=-1 - P.modeData.pt=0 - P.modeData.target=100 - int_grade=0 - grade_points=0 - rollGrades=0 - spd_lvl=0 - cools=0 - regrets=0 - prevSectTime=0 - isInRoll=false - isInRollTrans=false - prevDrop70=false - nextSpeedUp=false - coolList={false,false,false,false,false,false,false,false,false} - regretList={false,false,false,false,false,false,false,false,false,false} - local decayRate={125,80,80,50,45,45,45,40,40,40,40,40,30,30,30,20,20,20,20,20,15,15,15,15,15,15,15,15,15,15,10,10,10,9,9,9,8,8,8,7,7,7,6} - local decayTimer=0 + local D=P.modeData + D.regretDelay=-1 + D.pt=0 + D.target=100 + D.int_grade=0 + D.grade_points=0 + D.decayTimer=0 + D.rollGrades=0 + D.spd_lvl=0 + D.cools=0 + D.regrets=0 + D.prevSectTime=0 + D.isInRoll=false + D.isInRollTrans=false + D.prevDrop70=false + D.nextSpeedUp=false + D.coolList,D.regretList=TABLE.new(false,9),TABLE.new(false,10) + while true do - YIELD() - P.modeData.grade=getGrade() - P.modeData.gradePts=math.max(math.min(math.floor(int_grade_boosts[math.min(int_grade+1,#int_grade_boosts)]+rollGrades+cools+1-regrets),#gradeList),1) - if P.stat.frame-prevSectTime > reg_time[math.ceil(P.modeData.pt/100+0.01)] and not (isInRoll or isInRollTrans) then - regretList[math.ceil(P.modeData.pt/100)]=true + coroutine.yield() + D.gradePts=getCurrentGrade(D) + if P.stat.frame-D.prevSectTime > regret_time[getCurSection(D)] and not (D.isInRoll or D.isInRollTrans) then + D.regretList[math.ceil(D.pt/100)]=true end - if regretDelay>-1 then - regretDelay=regretDelay-1 - if regretDelay==-1 then P:_showText("REGRET!!",0,-120,80,'beat',.8) end + if D.regretDelay>-1 then + D.regretDelay=D.regretDelay-1 + if D.regretDelay==-1 then P:_showText("REGRET!!",0,-120,80,'beat',.8) end end - if isInRollTrans then + if D.isInRollTrans then if P.waiting>=220 then - --Make field invisible + -- Make field invisible for y=1,#P.field do for x=1,10 do P.visTime[y][x]=P.waiting-220 end end @@ -320,24 +331,22 @@ return{ P:_showText("1",0,-120,120,'fly',1) elseif P.waiting==1 then playReadySFX(0,1) - isInRollTrans=false - isInRoll=true + D.isInRollTrans=false + D.isInRoll=true BGM.play('hope') BG.set('blockspace') - prevSectTime=P.stat.frame + D.prevSectTime=P.stat.frame end end - if P.waiting<=0 and grade_points>0 and not isInRoll then - decayTimer=decayTimer+1 - if decayTimer>=decayRate[math.min(int_grade+1,#decayRate)] then - decayTimer=0 - grade_points=grade_points-1 + if P.waiting<=0 and D.grade_points>0 and P.combo<1 and not D.isInRoll then + D.decayTimer=D.decayTimer+1 + if D.decayTimer>=decayRate[math.min(D.internal_grade+1,#decayRate)] then + D.decayTimer=0 + D.grade_points=D.grade_points-1 end - elseif isInRoll and P.stat.frame>=prevSectTime+3599 then - rollGrades=rollGrades+(cools>8 and 1.6 or 0.5) - P.modeData.grade=getGrade() - P.modeData.gradePts=math.min(math.floor(int_grade_boosts[math.min(int_grade+1,#int_grade_boosts)]+rollGrades+cools+1-regrets),#gradeList) - YIELD() + elseif D.isInRoll and P.stat.frame>=D.prevSectTime+3599 then + D.rollGrades=D.rollGrades+(D.cools>8 and 1.6 or 0.5) + D.gradePts=getCurrentGrade(D) P:win('finish') end end diff --git a/parts/eventsets/master_h.lua b/parts/eventsets/master_h.lua index 8934830a0..6031405c4 100644 --- a/parts/eventsets/master_h.lua +++ b/parts/eventsets/master_h.lua @@ -2,7 +2,7 @@ local death_lock={12,11,10,9,8, 8,8,7,7,6} local death_wait={10,9, 8, 7,6, 7,6,6,5,5} local death_fall={10,9, 8, 7,6, 7,6,5,5,5} -return{ +return { drop=0, lock=death_lock[1], wait=death_wait[1], @@ -25,7 +25,7 @@ return{ if D.pt%100==99 then SFX.play('warn_1') - elseif D.pt>=D.target then--Level up! + elseif D.pt>=D.target then-- Level up! s=D.target/100 local E=P.gameEnv E.lock=death_lock[s] @@ -40,7 +40,7 @@ return{ elseif s==5 then if P.stat.frame>183*60 then D.pt=500 - P:win('finish') + P:torikanEnd(183) return else E.bone=true diff --git a/parts/eventsets/master_instinct.lua b/parts/eventsets/master_instinct.lua index 5a42febba..053e786ce 100644 --- a/parts/eventsets/master_instinct.lua +++ b/parts/eventsets/master_instinct.lua @@ -5,7 +5,7 @@ local inv_hide={20,17,14,11, 8, 5, 3, 2, 1, 0} local hidetimer=0 local held=false -return{ +return { drop=0, lock=inv_lock[1], wait=inv_wait[1], @@ -23,7 +23,7 @@ return{ if c==0 and D.pt%100==99 then if D.pt<1000 then hidetimer=0-inv_wait[(P.modeData.pt/100-(P.modeData.pt%100)/100)+1] - if c>0 then hidetimer=hidetimer-inv_fall[(P.modeData.pt/100-(P.modeData.pt%100)/100)+1]end + if c>0 then hidetimer=hidetimer-inv_fall[(P.modeData.pt/100-(P.modeData.pt%100)/100)+1] end end return end @@ -35,12 +35,12 @@ return{ held=false if D.pt<1000 then hidetimer=0-inv_wait[(P.modeData.pt/100-(P.modeData.pt%100)/100)+1] - if c>0 then hidetimer=hidetimer-inv_fall[(P.modeData.pt/100-(P.modeData.pt%100)/100)+1]end + if c>0 then hidetimer=hidetimer-inv_fall[(P.modeData.pt/100-(P.modeData.pt%100)/100)+1] end end if D.pt%100==99 then SFX.play('warn_1') - elseif D.pt>=D.target then--Level up! + elseif D.pt>=D.target then-- Level up! s=D.target/100 local E=P.gameEnv E.lock=inv_lock[s] @@ -72,13 +72,13 @@ return{ P.modeData.pt=0 P.modeData.target=100 while true do - YIELD() + coroutine.yield() if P.holdTime==0 and P.waiting<=0 and not held then hidetimer=0 held=true end hidetimer=hidetimer+1 - if hidetimer>inv_hide[(P.modeData.pt/100-(P.modeData.pt%100)/100)+1]then + if hidetimer>inv_hide[(P.modeData.pt/100-(P.modeData.pt%100)/100)+1] then P.gameEnv.block=false else P.gameEnv.block=true diff --git a/parts/eventsets/master_n.lua b/parts/eventsets/master_n.lua index ef573852c..5eda88ec3 100644 --- a/parts/eventsets/master_n.lua +++ b/parts/eventsets/master_n.lua @@ -2,7 +2,7 @@ local rush_lock={20,18,16,15,14, 14,13,12,11,11} local rush_wait={12,11,11,10,10, 10,10, 9, 9, 9} local rush_fall={18,16,14,13,12, 12,11,11,10,10} -return{ +return { drop=0, lock=rush_lock[1], wait=rush_wait[1], @@ -25,7 +25,7 @@ return{ if D.pt%100==99 then SFX.play('warn_1') - elseif D.pt>=D.target then--Level up! + elseif D.pt>=D.target then-- Level up! s=D.target/100 local E=P.gameEnv E.lock=rush_lock[s] @@ -38,9 +38,9 @@ return{ elseif s==4 then BG.set('rainbow2') elseif s==5 then - if P.stat.frame>260*60 then + if P.stat.time>260 then D.pt=500 - P:win('finish') + P:torikanEnd(260) return else P.gameEnv.freshLimit=10 diff --git a/parts/eventsets/master_ph.lua b/parts/eventsets/master_ph.lua index 917259290..0fc304f0a 100644 --- a/parts/eventsets/master_ph.lua +++ b/parts/eventsets/master_ph.lua @@ -18,9 +18,9 @@ return if p>=P.modeData.target then local ENV=P.gameEnv local T=P.modeData.target - --Stage 1: clear 3 techrash - if T==12 then--Stage 2: swap color of S/Z & J/L - P:stageComplete(2) + -- Stage 1: clear 3 techrash + if T==12 then-- Stage 2: swap color of S/Z & J/L + P:stageComplete(1) P.waiting=30 P.curMission=false @@ -34,9 +34,9 @@ return P.modeData.target=26 SFX.play('reach') - elseif T==26 then--Stage 3: dig to bottom - P:stageComplete(3) - if not P.holdQueue[1]then--1 up if ban hold + elseif T==26 then-- Stage 3: dig to bottom + P:stageComplete(2) + if not P.holdQueue[1] then-- 1 up if ban hold P.life=P.life+1 end P.waiting=45 @@ -44,7 +44,7 @@ return ENV.skin[3],ENV.skin[4]=ENV.skin[4],ENV.skin[3] for i=1,10 do - if P.field[i]then + if P.field[i] then for j=1,10 do if P.field[i][j]>0 then P.field[i][j]=17 @@ -79,9 +79,9 @@ return P.modeData.target=42 SFX.play('reach') - elseif T==42 then--Stage 4: survive in high speed + elseif T==42 then-- Stage 4: survive in high speed if P.garbageBeneath==0 then - P:stageComplete(4) + P:stageComplete(3) P.waiting=30 ENV.lock=11 P:setNext(6) @@ -93,8 +93,8 @@ return else p=41 end - elseif T==62 then--Stage 5: survive without easy-fresh rule - P:stageComplete(5) + elseif T==62 then-- Stage 5: survive without easy-fresh rule + P:stageComplete(4) P.life=P.life+1 ENV.lock=13 ENV.wait=5 @@ -104,8 +104,8 @@ return P.modeData.target=126 SFX.play('reach') - elseif T==126 then--Stage 6: speed up - P:stageComplete(6) + elseif T==126 then-- Stage 6: speed up + P:stageComplete(5) P.life=P.life+1 ENV.lock=11 @@ -114,8 +114,8 @@ return P.modeData.target=162 SFX.play('reach') - elseif T==162 then--Stage 7: speed up+++ - P:stageComplete(7) + elseif T==162 then-- Stage 7: speed up+++ + P:stageComplete(6) P.life=P.life+1 ENV.lock=10 @@ -125,8 +125,8 @@ return P.modeData.target=226 SFX.play('reach') - elseif T==226 then--Stage 8: final invisible - P:stageComplete(8) + elseif T==226 then-- Stage 8: final invisible + P:stageComplete(7) P.life=P.life+1 ENV.bone=false @@ -134,10 +134,10 @@ return P.modeData.target=259 SFX.play('reach') - elseif T==259 then--Stage 9: ending - P:stageComplete(9) + elseif T==259 then-- Stage 9: ending + P:stageComplete(8) P.life=P.life+1 - for i=1,7 do ENV.skin[i]=P.holeRND:random(16)end + for i=1,7 do ENV.skin[i]=P.holeRND:random(16) end P:setInvisible(40) ENV.lock=15 diff --git a/parts/eventsets/pc_inf.lua b/parts/eventsets/pc_inf.lua index 21f6d264a..e5bef0112 100644 --- a/parts/eventsets/pc_inf.lua +++ b/parts/eventsets/pc_inf.lua @@ -1,8 +1,8 @@ -return{ +return { heightLimit=4, mesDisp=function(P) setFont(60) - mStr(P.stat.pc,63,340) + GC.mStr(P.stat.pc,63,340) mText(TEXTOBJ.pc,63,410) end, hook_drop=function(P) diff --git a/parts/eventsets/pctrain_l.lua b/parts/eventsets/pctrain_l.lua index 2e83cf720..71ce7a170 100644 --- a/parts/eventsets/pctrain_l.lua +++ b/parts/eventsets/pctrain_l.lua @@ -11,7 +11,7 @@ local function task_PC(P) P:pushNextList(L,symmetry) P.control=false - if P.frameRun>180 then for _=1,26 do YIELD()end end + if P.frameRun>180 then for _=1,26 do coroutine.yield() end end P.control=true local base=PCbase[difficulty] @@ -31,9 +31,9 @@ local function _check(P) if P.stat.pc%4==0 and P.stat.pc>0 and P.stat.pc<=40 then local s=P.stat.pc/4 - P.gameEnv.drop=pc_drop[s]or 10 - P.gameEnv.lock=pc_lock[s]or 25 - P.gameEnv.fall=pc_fall[s]or 4 + P.gameEnv.drop=pc_drop[s] or 10 + P.gameEnv.lock=pc_lock[s] or 25 + P.gameEnv.fall=pc_fall[s] or 4 if s==10 then P:_showText(text.maxspeed,0,-140,100,'appear',.6) end @@ -41,14 +41,15 @@ local function _check(P) end end end -return{ +return { sequence='none', RS="SRS", mesDisp=function(P) setFont(60) - mStr(P.stat.pc,63,260) + GC.mStr(P.stat.pc,63,260) mText(TEXTOBJ.pc,63,330) end, hook_drop=_check, - task=_check,--Just run one time at first to start first level + task=_check,-- Just run one time at first to start first level + CCIncompatible=true, } diff --git a/parts/eventsets/pctrain_n.lua b/parts/eventsets/pctrain_n.lua index a925725ba..965eb9f7d 100644 --- a/parts/eventsets/pctrain_n.lua +++ b/parts/eventsets/pctrain_n.lua @@ -8,13 +8,13 @@ local PCtype={ 1,2,3, } local function task_PC(P) - local difficulty=PCtype[P.stat.pc+1]or 3 + local difficulty=PCtype[P.stat.pc+1] or 3 local L=PClist[difficulty][P.holeRND:random(#PClist[difficulty])] local symmetry=P.holeRND:random()>.5 P:pushNextList(L,symmetry) P.control=false - if P.frameRun>180 then for _=1,26 do YIELD()end end + if P.frameRun>180 then for _=1,26 do coroutine.yield() end end P.control=true local base=PCbase[difficulty] @@ -34,14 +34,15 @@ local function _check(P) end end end -return{ +return { sequence='none', RS="SRS", mesDisp=function(P) setFont(60) - mStr(P.stat.pc,63,260) + GC.mStr(P.stat.pc,63,260) mText(TEXTOBJ.pc,63,330) end, hook_drop=_check, - task=_check,--Just run one time at first to start first level + task=_check,-- Just run one time at first to start first level + CCIncompatible=true, } diff --git a/parts/eventsets/royale.lua b/parts/eventsets/royale.lua index fae53599c..71922b573 100644 --- a/parts/eventsets/royale.lua +++ b/parts/eventsets/royale.lua @@ -1,10 +1,4 @@ -local gc=love.graphics -local gc_draw,gc_print,gc_setColor=gc.draw,gc.print,gc.setColor -local setFont=setFont - -local PLAYERS,PLY_ALIVE=PLAYERS,PLY_ALIVE - -return{ +return { layout='royale', fkey1=function(P) P:changeAtkMode(P.atkMode<3 and P.atkMode+2 or 5-P.atkMode) @@ -12,20 +6,20 @@ return{ end, mesDisp=function(P) setFont(35) - mStr(#PLY_ALIVE.."/"..#PLAYERS,63,175) - mStr(P.modeData.ko,80,215) - gc_draw(TEXTOBJ.ko,60-TEXTOBJ.ko:getWidth(),222) + GC.mStr(#PLY_ALIVE.."/"..#PLAYERS,63,175) + GC.mStr(P.modeData.ko,80,215) + GC.draw(TEXTOBJ.ko,60-TEXTOBJ.ko:getWidth(),222) setFont(20) - gc_setColor(1,.5,0,.6) - gc_print(P.badge,103,227) - gc_setColor(.97,.97,.97) + GC.setColor(1,.5,0,.6) + GC.print(P.badge,103,227) + GC.setColor(.97,.97,.97) setFont(25) - mStr(text.powerUp[P.strength],63,290) - gc_setColor(1,1,1) + GC.mStr(text.powerUp[P.strength],63,290) + GC.setColor(1,1,1) for i=1,P.strength do - gc_draw(IMG.badgeIcon,16*i+6,260) + GC.draw(IMG.badgeIcon,16*i+6,260) end end, } diff --git a/parts/eventsets/secret_grade.lua b/parts/eventsets/secret_grade.lua deleted file mode 100644 index f72156f9a..000000000 --- a/parts/eventsets/secret_grade.lua +++ /dev/null @@ -1,90 +0,0 @@ -local gc_setColor,gc_draw=love.graphics.setColor,love.graphics.draw -local ply_applyField=PLY.draw.applyField -local function getOpenHole(num) - return -math.abs(((num-1) % 18)-9)+10 -end -local F={} - --- local ranks={"10","9","8","7","6","5","4","3","2","1","S1","S2","S3","S4","S5","S6","S7","S8","S9","GM","GM+","TM","TM+","TM+₂","TM+₃", "TM+₄","TM+₅"} --- lines: 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 26 - -local function getSmallNum(num) - local smalldigit={[0]="₀","₁","₂","₃","₄","₅","₆","₇","₈","₉"} - local str=tostring(num) - local out="" - for i=1,#str do - out=out..smalldigit[tonumber(string.sub(str,i,i))] - end - return out -end - -local function getRank(index) - if index<11 then -- rank 10 - 1 - return tostring(11-index) - elseif index<20 then -- S1 - S9 ranks - return "S"..index-10 - elseif index<24 then -- GM, GM+, TM, TM+ ranks - local r={"GM","GM+","TM","TM+"} - return r[index-19] - else - return "TM+"..getSmallNum(index-22) - end -end - -local function generateGuide(num) - local l=#F - if l>num then - return - end - for i=l,num do - F[i] = {} - local h=getOpenHole(i) - for j=1,10 do - F[i][j]=h==j and -1 or 21 - end - end -end - -return{ - fkey1=function(P)P.modeData.showGuide=not P.modeData.showGuide end, - mesDisp=function(P) - mText(TEXTOBJ.grade,63,190) - mText(TEXTOBJ.line,63,310) - setFont(55) - mStr(getRank(P.modeData.rankPts),63,125) - mStr(P.modeData.rankPts-1,63,245) - ply_applyField(P) - local mark=TEXTURE.puzzleMark - gc_setColor(1,1,1) - if P.modeData.showGuide then - for y=1,P.modeData.rankPts+1 do for x=1,10 do - local T=F[y][x] - if T~=0 then - gc_draw(mark[T],30*x-30,600-30*y) - end - end end - end - PLY.draw.cancelField(P) - end, - task=function(P) - P.modeData.rankPts=1 - P.modeData.showGuide=true - generateGuide(10) - end, - hook_drop=function(P) - local D=P.modeData - D.rankPts=1 - for i=1,#P.field do - local h=getOpenHole(i) - for j=1,10 do - if P.field[i][j]>0 and h==j then goto post_pts_calc end - if P.field[i][j]==0 and h~=j then goto post_pts_calc end - end - if i==#P.field then goto post_pts_calc end - if P.field[i+1][h]==0 then goto post_pts_calc end - D.rankPts=D.rankPts+1 - end - ::post_pts_calc:: - generateGuide(D.rankPts+20) - end -} diff --git a/parts/eventsets/sprintEff_40.lua b/parts/eventsets/sprintEff_40.lua index 09d76a226..412e8e6e2 100644 --- a/parts/eventsets/sprintEff_40.lua +++ b/parts/eventsets/sprintEff_40.lua @@ -1,15 +1,15 @@ -return{ +return { mesDisp=function(P) setFont(45) - mStr(("%.1f"):format(P.stat.atk),63,270) + GC.mStr(("%d"):format(P.stat.atk),63,270) mText(TEXTOBJ.atk,63,323) - mStr(("%.2f"):format(P.stat.atk/P.stat.row),63,370) + GC.mStr(("%.2f"):format(P.stat.atk/P.stat.row),63,370) mText(TEXTOBJ.eff,63,423) setFont(55) local r=40-P.stat.row if r<0 then r=0 end - mStr(r,63,170) + GC.mStr(r,63,170) PLY.draw.drawTargetLine(P,r) end, hook_drop=function(P) diff --git a/parts/eventsets/sprintMD.lua b/parts/eventsets/sprintMD.lua index 0c18a7e1a..ddb66b52c 100644 --- a/parts/eventsets/sprintMD.lua +++ b/parts/eventsets/sprintMD.lua @@ -1,27 +1,27 @@ -return{ +return { mesDisp=function(P) setFont(55) local r=40-P.stat.row if r<0 then r=0 end - mStr(r,63,265) + GC.mStr(r,63,265) PLY.draw.drawTargetLine(P,r) end, task=function(P) - YIELD() + coroutine.yield() while true do - for _=1,P.holeRND:random(40,200)do YIELD()end + for _=1,P.holeRND:random(40,200) do coroutine.yield() end local r=P.holeRND:random(7) if r==1 then - if P.cur and not P:ifoverlap(P.cur.bk,P.curX-1,P.curY)then + if P.cur and not P:ifoverlap(P.cur.bk,P.curX-1,P.curY) then P:createMoveFX('left') P.curX=P.curX-1 - P:freshBlock('move') + P:freshMoveBlock() end elseif r==2 then - if P.cur and not P:ifoverlap(P.cur.bk,P.curX-1,P.curY)then + if P.cur and not P:ifoverlap(P.cur.bk,P.curX-1,P.curY) then P:createMoveFX('left') P.curX=P.curX-1 - P:freshBlock('move') + P:freshMoveBlock() end elseif r==3 then P:act_rotRight() diff --git a/parts/eventsets/sprintSym.lua b/parts/eventsets/sprintSym.lua index 2457a189a..afe90dfc4 100644 --- a/parts/eventsets/sprintSym.lua +++ b/parts/eventsets/sprintSym.lua @@ -1,7 +1,7 @@ local oppo={ [1]=7,[7]=1,[11]=3,[3]=11,[14]=14,[4]=4,[9]=9, } -return{ +return { skin={ 1,7,11,3,14,4,9, 1,7,2,6,10,2,13,5,9,15,10,11,3,12,2,16,8,4, @@ -11,7 +11,7 @@ return{ setFont(55) local r=40-P.stat.row if r<0 then r=0 end - mStr(r,63,265) + GC.mStr(r,63,265) PLY.draw.drawTargetLine(P,r) end, hook_drop=function(P) @@ -19,7 +19,7 @@ return{ for y=1,#F do local l=F[y] for x=1,5 do - if l[x]>0 and l[11-x]>0 and oppo[l[x]]~=l[11-x]then + if l[x]>0 and l[11-x]>0 and oppo[l[x]]~=l[11-x] then P:lose() return end diff --git a/parts/eventsets/sprint_finesse_lock.lua b/parts/eventsets/sprint_finesse_lock.lua new file mode 100644 index 000000000..36ee2e921 --- /dev/null +++ b/parts/eventsets/sprint_finesse_lock.lua @@ -0,0 +1,115 @@ +local function lockKey(P,keys) + for _,v in next,keys do + P.keyAvailable[v]=false + VK.keys[v].ava=false + VK.release(v) + P:releaseKey(v) + end +end +local function unlockKey(P,keys) + for _,v in next,keys do + P.keyAvailable[v]=true + VK.keys[v].ava=true + end +end +local function lockMovement(P) + lockKey(P,{1,2}) +end +local function lockRotation(P) + lockKey(P,{3,4}) +end +local function lockRotation180(P) + lockKey(P,{5}) +end +local function unlock(P) + if P.cur and P.cur.name==6 and not P.gameEnv.skipOCheck then -- don't unlock rotation if O piece & no O-spin + unlockKey(P,{1,2,6,7}) + return + end + unlockKey(P,{1,2,3,4,5,6,7}) +end +local function resetLock(P) + unlock(P) + unlockKey(P,{8}) + + P.modeData.moveCount=0 + P.modeData.rotations=0 + P.holdTime=1 +end + +local function onMove(P) + if not P.cur then return end + + P.holdTime=0 + lockKey(P,{8}) + + if not P:_roofCheck() then + P.modeData.moveCount=P.modeData.moveCount+1 + if P.modeData.moveCount>=2 then lockMovement(P) end + end +end +local function onAutoMove(P) + if P:_roofCheck() then unlock(P) end +end +local function onRotate(P,dir) + if not P.cur then return end + + P.holdTime=0 + lockKey(P,{8}) + + if not P:_roofCheck() then + P.modeData.rotations=P.modeData.rotations+(dir==2 and 2 or 1) + lockRotation180(P) + if P.modeData.rotations>=2 then + lockRotation(P) + end + end +end + +return { + arr=0, + fineKill=true, + mesDisp=function(P) + setFont(55) + local r=40-P.stat.row + if r<0 then r=0 end + GC.mStr(r,63,265) + PLY.draw.drawTargetLine(P,r) + end, + task=function(P) + resetLock(P) + local RSname=P.RS.name + P.gameEnv.skipOCheck=( + string.find(RSname,'TRS') or + string.find(RSname,'BiRS') or + string.find(RSname,'ASC') + ) + end, + hook_drop=function(P) + resetLock(P) + if P.stat.row>=40 then + P:win('finish') + end + end, + hook_spawn=function(P) + if P.gameEnv.skipOCheck then return end + if P.cur.name==6 then + lockRotation(P) + lockRotation180(P) + else + resetLock(P) + end + end, + hook_hold=function(P) + if P.gameEnv.skipOCheck then return end + if P.cur.name==6 then + lockRotation(P) + lockRotation180(P) + else + resetLock(P) + end + end, + hook_left_manual=onMove, hook_right_manual=onMove, + hook_left_auto=onAutoMove, hook_right_auto=onAutoMove, + hook_rotate=onRotate +} diff --git a/parts/eventsets/stack_e.lua b/parts/eventsets/stack_e.lua index 98e599c82..3b43e5fdb 100644 --- a/parts/eventsets/stack_e.lua +++ b/parts/eventsets/stack_e.lua @@ -1,9 +1,9 @@ -return{ +return { fieldH=20, fillClear=false, mesDisp=function(P) setFont(60) - mStr(P.stat.row,63,280) + GC.mStr(P.stat.row,63,280) mText(TEXTOBJ.line,63,350) PLY.draw.drawMarkLine(P,20,.3,1,1,TIME()%.42<.21 and .95 or .6) end, @@ -23,8 +23,10 @@ return{ LINE.discard(table.remove(P.field,1)) LINE.discard(table.remove(P.visTime,1)) end + P.garbageBeneath=P.garbageBeneath-bonus end end + P:freshBlockGhost() end end, } \ No newline at end of file diff --git a/parts/eventsets/stack_u.lua b/parts/eventsets/stack_u.lua index c2504fcf8..869e6f5ed 100644 --- a/parts/eventsets/stack_u.lua +++ b/parts/eventsets/stack_u.lua @@ -1,9 +1,9 @@ -return{ +return { fieldH=21, fillClear=false, mesDisp=function(P) setFont(60) - mStr(P.stat.row,63,280) + GC.mStr(P.stat.row,63,280) mText(TEXTOBJ.line,63,350) PLY.draw.drawMarkLine(P,17,.3,1,1,TIME()%.42<.21 and .95 or .6) end, @@ -23,8 +23,10 @@ return{ LINE.discard(table.remove(P.field,1)) LINE.discard(table.remove(P.visTime,1)) end + P.garbageBeneath=P.garbageBeneath-bonus end end + P:freshBlockGhost() end end, } \ No newline at end of file diff --git a/parts/eventsets/survivor_e.lua b/parts/eventsets/survivor_e.lua index 0abdad615..ec098e09e 100644 --- a/parts/eventsets/survivor_e.lua +++ b/parts/eventsets/survivor_e.lua @@ -1,16 +1,16 @@ -return{ +return { mesDisp=function(P) setFont(60) - mStr(P.modeData.wave,63,310) + GC.mStr(P.modeData.wave,63,310) mText(TEXTOBJ.wave,63,375) end, task=function(P) while true do - YIELD() + coroutine.yield() if P.control then local D=P.modeData D.timer=D.timer+1 - if D.timer>=math.max(60,150-2*D.wave)and P.atkBufferSum<4 then + if D.timer>=math.max(60,150-2*D.wave) and P.atkBufferSum<4 then if D.wave==100 then P:win('finish') else diff --git a/parts/eventsets/survivor_h.lua b/parts/eventsets/survivor_h.lua index fce75feff..9e078c2f1 100644 --- a/parts/eventsets/survivor_h.lua +++ b/parts/eventsets/survivor_h.lua @@ -1,16 +1,16 @@ -return{ +return { mesDisp=function(P) setFont(60) - mStr(P.modeData.wave,63,310) + GC.mStr(P.modeData.wave,63,310) mText(TEXTOBJ.wave,63,375) end, task=function(P) while true do - YIELD() + coroutine.yield() if P.control then local D=P.modeData D.timer=D.timer+1 - if D.timer>=math.max(60,180-2*D.wave)and P.atkBufferSum<15 then + if D.timer>=math.max(60,180-2*D.wave) and P.atkBufferSum<15 then if D.wave==90 then P:win('finish') else diff --git a/parts/eventsets/survivor_l.lua b/parts/eventsets/survivor_l.lua index ab0a951f5..e0999f933 100644 --- a/parts/eventsets/survivor_l.lua +++ b/parts/eventsets/survivor_l.lua @@ -1,16 +1,16 @@ -return{ +return { mesDisp=function(P) setFont(60) - mStr(P.modeData.wave,63,310) + GC.mStr(P.modeData.wave,63,310) mText(TEXTOBJ.wave,63,375) end, task=function(P) while true do - YIELD() + coroutine.yield() if P.control then local D=P.modeData D.timer=D.timer+1 - if D.timer>=math.max(60,150-D.wave)and P.atkBufferSum<20 then + if D.timer>=math.max(60,150-D.wave) and P.atkBufferSum<20 then if D.wave==110 then P:win('finish') else @@ -18,7 +18,7 @@ return{ table.insert(P.atkBuffer,{line=generateLine(P.holeRND:random(10)),amount=4,countdown=t,cd0=t,time=0,sent=false,lv=3}) P.atkBufferSum=P.atkBufferSum+4 P.stat.recv=P.stat.recv+4 - if D.wave==60 then + if D.wave==90 then P:_showText(text.maxspeed,0,-140,100,'appear',.6) end P:shakeField(3) diff --git a/parts/eventsets/survivor_n.lua b/parts/eventsets/survivor_n.lua index 80ac120fe..82393088e 100644 --- a/parts/eventsets/survivor_n.lua +++ b/parts/eventsets/survivor_n.lua @@ -1,24 +1,24 @@ -return{ +return { mesDisp=function(P) setFont(60) - mStr(P.modeData.wave,63,310) + GC.mStr(P.modeData.wave,63,310) mText(TEXTOBJ.wave,63,375) end, task=function(P) while true do - YIELD() + coroutine.yield() if P.control then local D=P.modeData D.timer=D.timer+1 - if D.timer>=math.max(90,180-2*D.wave)and P.atkBufferSum<8 then + if D.timer>=math.max(90,180-2*D.wave) and P.atkBufferSum<8 then if D.wave==80 then P:win('finish') else local d=D.wave+1 table.insert(P.atkBuffer, - d%4==0 and{line=generateLine(P.holeRND:random(10)),amount=1,countdown=60,cd0=60,time=0,sent=false,lv=1}or - d%4==1 and{line=generateLine(P.holeRND:random(10)),amount=2,countdown=70,cd0=70,time=0,sent=false,lv=1}or - d%4==2 and{line=generateLine(P.holeRND:random(10)),amount=3,countdown=80,cd0=80,time=0,sent=false,lv=2}or + d%4==0 and{line=generateLine(P.holeRND:random(10)),amount=1,countdown=60,cd0=60,time=0,sent=false,lv=1} or + d%4==1 and{line=generateLine(P.holeRND:random(10)),amount=2,countdown=70,cd0=70,time=0,sent=false,lv=1} or + d%4==2 and{line=generateLine(P.holeRND:random(10)),amount=3,countdown=80,cd0=80,time=0,sent=false,lv=2} or d%4==3 and{line=generateLine(P.holeRND:random(10)),amount=4,countdown=90,cd0=90,time=0,sent=false,lv=3} ) P.atkBufferSum=P.atkBufferSum+d%4+1 diff --git a/parts/eventsets/survivor_u.lua b/parts/eventsets/survivor_u.lua index 161446189..0ab93cbff 100644 --- a/parts/eventsets/survivor_u.lua +++ b/parts/eventsets/survivor_u.lua @@ -1,16 +1,16 @@ -return{ +return { mesDisp=function(P) setFont(60) - mStr(P.modeData.wave,63,310) + GC.mStr(P.modeData.wave,63,310) mText(TEXTOBJ.wave,63,375) end, task=function(P) while true do - YIELD() + coroutine.yield() if P.control then local D=P.modeData D.timer=D.timer+1 - if D.timer>=math.max(300,600-10*D.wave)and P.atkBufferSum<20 then + if D.timer>=math.max(300,600-10*D.wave) and P.atkBufferSum<20 then if D.wave==35 then P:win('finish') else diff --git a/parts/eventsets/techrash_n.lua b/parts/eventsets/techrash_n.lua index f92000069..b78fdb0a5 100644 --- a/parts/eventsets/techrash_n.lua +++ b/parts/eventsets/techrash_n.lua @@ -1,7 +1,7 @@ -return{ +return { mesDisp=function(P) setFont(60) - mStr(P.stat.clear[7][4],63,250) + GC.mStr(P.stat.clear[7][4],63,250) mText(TEXTOBJ.techrash,63,315) end, hook_drop=function(P) diff --git a/parts/eventsets/techrash_u.lua b/parts/eventsets/techrash_u.lua index b32e99be6..70c3d28f7 100644 --- a/parts/eventsets/techrash_u.lua +++ b/parts/eventsets/techrash_u.lua @@ -1,23 +1,21 @@ -local gc=love.graphics - -return{ +return { mesDisp=function(P) setFont(60) - mStr(P.modeData.techrash,63,250) + GC.mStr(P.modeData.techrash,63,250) mText(TEXTOBJ.techrash,63,315) PLY.draw.applyField(P) local L=P.modeData.history for i=1,#L do - gc.setColor(1,.3,.3,.5-i*.04) - gc.rectangle('fill',30*L[i]-30,0,30,600) + GC.setColor(1,.3,.3,.5-i*.04) + GC.rectangle('fill',30*L[i]-30,0,30,600) end - PLY.draw.cancelField(P) + PLY.draw.cancelField() end, hook_drop=function(P) local C=P.lastPiece if C.row>0 then if C.row==4 then - if TABLE.find(P.modeData.history,C.curX)then + if TABLE.find(P.modeData.history,C.curX) then P:showText("STACK",0,-140,40,'flicker',.3) P:lose() else diff --git a/parts/eventsets/tsd_e.lua b/parts/eventsets/tsd_e.lua index cc6e450a9..b8583db4d 100644 --- a/parts/eventsets/tsd_e.lua +++ b/parts/eventsets/tsd_e.lua @@ -1,7 +1,7 @@ -return{ +return { mesDisp=function(P) setFont(60) - mStr(P.modeData.tsd,63,250) + GC.mStr(P.modeData.tsd,63,250) mText(TEXTOBJ.tsd,63,315) end, hook_drop=function(P) diff --git a/parts/eventsets/tsd_h.lua b/parts/eventsets/tsd_h.lua index d028b00fc..3f8c816d9 100644 --- a/parts/eventsets/tsd_h.lua +++ b/parts/eventsets/tsd_h.lua @@ -1,16 +1,14 @@ -local gc=love.graphics - -return{ +return { mesDisp=function(P) setFont(60) - mStr(P.modeData.tsd,63,250) + GC.mStr(P.modeData.tsd,63,250) mText(TEXTOBJ.tsd,63,315) local L=P.modeData.history - if L[1]and L[1]==L[2]and L[1]==L[3]then + if L[1] and L[1]==L[2] and L[1]==L[3] then PLY.draw.applyField(P) - gc.setColor(1,.3,.3,.2) - gc.rectangle('fill',30*L[1]-30,0,30,600) - PLY.draw.cancelField(P) + GC.setColor(1,.3,.3,.2) + GC.rectangle('fill',30*L[1]-30,0,30,600) + PLY.draw.cancelField() end end, hook_drop=function(P) @@ -18,7 +16,7 @@ return{ if C.row>0 then if C.id==5 and C.row==2 and C.spin then local L=P.modeData.history - if L[1]==C.centX and L[1]==L[2]and L[1]==L[3]then + if L[1]==C.centX and L[1]==L[2] and L[1]==L[3] then P:showText("STACK",0,-140,40,'flicker',.3) P:lose() else diff --git a/parts/eventsets/tsd_u.lua b/parts/eventsets/tsd_u.lua index 09f95d282..c2e6d9c67 100644 --- a/parts/eventsets/tsd_u.lua +++ b/parts/eventsets/tsd_u.lua @@ -1,23 +1,21 @@ -local gc=love.graphics - -return{ +return { mesDisp=function(P) setFont(60) - mStr(P.modeData.tsd,63,250) + GC.mStr(P.modeData.tsd,63,250) mText(TEXTOBJ.tsd,63,315) PLY.draw.applyField(P) local L=P.modeData.history for i=1,#L do - gc.setColor(1,.3,.3,.4-i*.05) - gc.rectangle('fill',30*L[i]-30,0,30,600) + GC.setColor(1,.3,.3,.4-i*.05) + GC.rectangle('fill',30*L[i]-30,0,30,600) end - PLY.draw.cancelField(P) + PLY.draw.cancelField() end, hook_drop=function(P) local C=P.lastPiece if C.row>0 then if C.id==5 and C.row==2 and C.spin then - if TABLE.find(P.modeData.history,C.centX)then + if TABLE.find(P.modeData.history,C.centX) then P:showText("STACK",0,-140,40,'flicker',.3) P:lose() else diff --git a/parts/eventsets/ultra.lua b/parts/eventsets/ultra.lua index 1f19b6a74..cd085dd12 100644 --- a/parts/eventsets/ultra.lua +++ b/parts/eventsets/ultra.lua @@ -1,29 +1,28 @@ -local gc=love.graphics local warnTime={60,90,105,115,116,117,118,119,120} for i=1,#warnTime do warnTime[i]=warnTime[i]*60 end -return{ +return { mesDisp=function(P) - gc.setLineWidth(2) - gc.setColor(.98,.98,.98,.8) - gc.rectangle('line',0,260,126,80,4) - gc.setColor(.98,.98,.98,.4) - gc.rectangle('fill',0+2,260+2,126-4,80-4,2) + GC.setLineWidth(2) + GC.setColor(.98,.98,.98,.8) + GC.rectangle('line',0,260,126,80,4) + GC.setColor(.98,.98,.98,.4) + GC.rectangle('fill',0+2,260+2,126-4,80-4,2) setFont(45) local t=P.stat.frame/60 local T=("%.1f"):format(120-t) - gc.setColor(COLOR.dH) - mStr(T,65,270) + GC.setColor(COLOR.dH) + GC.mStr(T,65,270) t=t/120 - gc.setColor(1.7*t,2.3-2*t,.3) - mStr(T,63,268) + GC.setColor(1.7*t,2.3-2*t,.3) + GC.mStr(T,63,268) end, task=function(P) - BGM.seek(0) + BGM.set('all','seek',0) P.modeData.section=1 while true do - YIELD() - while P.stat.frame>=warnTime[P.modeData.section]do + coroutine.yield() + while P.stat.frame>=warnTime[P.modeData.section] do if P.modeData.section<9 then P.modeData.section=P.modeData.section+1 playReadySFX(3,.7+P.modeData.section*.03) diff --git a/parts/fonts/monospaced.otf b/parts/fonts/monospaced.otf index 4db014dd5..2be4ba648 100644 Binary files a/parts/fonts/monospaced.otf and b/parts/fonts/monospaced.otf differ diff --git a/parts/fonts/proportional.otf b/parts/fonts/proportional.otf index f0103be6f..66f97a15b 100644 Binary files a/parts/fonts/proportional.otf and b/parts/fonts/proportional.otf differ diff --git a/parts/gameFuncs.lua b/parts/gameFuncs.lua index 8c8476258..c60c82905 100644 --- a/parts/gameFuncs.lua +++ b/parts/gameFuncs.lua @@ -1,22 +1,20 @@ -local gc=love.graphics -local gc_push,gc_pop=gc.push,gc.pop -local gc_origin,gc_replaceTransform=gc.origin,gc.replaceTransform -local gc_setLineWidth,gc_setColor=gc.setLineWidth,gc.setColor -local gc_setShader=gc.setShader -local gc_draw,gc_rectangle,gc_line,gc_printf=gc.draw,gc.rectangle,gc.line,gc.printf +local gc_push,gc_pop=GC.push,GC.pop +local gc_origin,gc_replaceTransform=GC.origin,GC.replaceTransform +local gc_setLineWidth,gc_setColor=GC.setLineWidth,GC.setColor +local gc_setShader=GC.setShader +local gc_draw,gc_rectangle,gc_printf=GC.draw,GC.rectangle,GC.printf local ins,rem=table.insert,table.remove -local int,rnd=math.floor,math.random +local floor,rnd=math.floor,math.random local approach=MATH.expApproach local SETTING,GAME,SCR=SETTING,GAME,SCR local PLAYERS=PLAYERS -local playSFX=SFX.play ---System -do--function tryBack() +------------------------------[System]------------------------------ +do-- function tryBack() local sureTime=-1e99 function tryBack() if TIME()-sureTime<1 then @@ -28,7 +26,7 @@ do--function tryBack() end end end -do--function tryReset() +do-- function tryReset() local sureTime=-1e99 function tryReset() if TIME()-sureTime<1 then @@ -40,7 +38,7 @@ do--function tryReset() end end end -do--function tryDelete() +do-- function tryDelete() local sureTime=-1e99 function tryDelete() if TIME()-sureTime<1 then @@ -52,21 +50,21 @@ do--function tryDelete() end end end -do--function loadFile(name,args), function saveFile(data,name,args) - local t=setmetatable({},{__index=function()return"'$1' loading failed: $2"end}) +do-- function loadFile(name,args), function saveFile(data,name,args) + local t=setmetatable({},{__index=function() return "'$1' loading failed: $2" end}) function loadFile(name,args) local text=text or t - if not args then args=''end + if not args then args='' end local res,mes=pcall(FILE.load,name,args) if res then return mes else - if mes:find'open error'then + if mes:find'open error' then MES.new('error',text.loadError_open:repD(name,"")) - elseif mes:find'unknown mode'then + elseif mes:find'unknown mode' then MES.new('error',text.loadError_errorMode:repD(name,args)) - elseif mes:find'no file'then - if not args:sArg'-canSkip'then + elseif mes:find'no file' then + if not args:sArg'-canSkip' then MES.new('error',text.loadError_noFile:repD(name,"")) end elseif mes then @@ -83,24 +81,17 @@ do--function loadFile(name,args), function saveFile(data,name,args) return true else MES.new('error', - mes:find'duplicate'and - text.saveError_duplicate:repD(name)or - mes:find'encode error'and - text.saveError_encode:repD(name)or + mes:find'duplicate' and + text.saveError_duplicate:repD(name) or + mes:find'encode error' and + text.saveError_encode:repD(name) or mes and - text.saveError_other:repD(name,mes)or + text.saveError_other:repD(name,mes) or text.saveError_unknown:repD(name) ) end end end -function isSafeFile(file,mes) - if love.filesystem.getRealDirectory(file)~=SAVEDIR then - return true - elseif mes then - MES.new('warn',mes) - end -end function saveStats() return saveFile(STAT,'conf/data') end @@ -108,9 +99,15 @@ function saveProgress() return saveFile(RANKS,'conf/unlock') end function saveSettings() + if WS.status('game')=='running' then + NET.player_updateConf() + end return saveFile(SETTING,'conf/settings') end -do--function applySettings() +function saveUser() + return saveFile(USER.__data,'conf/user') +end +do-- function applySettings() local saturateValues={ normal={0,1}, soft={.2,.7}, @@ -118,68 +115,75 @@ do--function applySettings() light={.2,.8}, color={-.2,1.2}, } - function applySettings() - --Apply language + function applySettings(reason) + -- Apply language text=LANG.get(SETTING.locale) WIDGET.setLang(text.WidgetText) for k,v in next,TEXTOBJ do - if rawget(text,k)then + if rawget(text,k) then v:set(text[k]) end end - --Apply cursor + -- Apply cursor love.mouse.setVisible(SETTING.sysCursor) - --Apply fullscreen + -- Apply fullscreen love.window.setFullscreen(SETTING.fullscreen) - love.resize(gc.getWidth(),gc.getHeight()) + love.resize(GC.getWidth(),GC.getHeight()) - --Apply Zframework setting + -- Apply Zframework setting Z.setClickFX(SETTING.clickFX) Z.setFrameMul(SETTING.frameMul) Z.setPowerInfo(SETTING.powerInfo) Z.setCleanCanvas(SETTING.cleanCanvas) - --Apply VK shape + -- Apply VK shape VK.setShape(SETTING.VKSkin) - --Apply sound + -- Apply sound love.audio.setVolume(SETTING.mainVol) BGM.setVol(SETTING.bgm) SFX.setVol(SETTING.sfx) VOC.setVol(SETTING.voc) - --Apply saturs + -- Apply saturs local m - m=saturateValues[SETTING.blockSatur]or saturateValues.normal + m=saturateValues[SETTING.blockSatur] or saturateValues.normal SHADER.blockSatur:send('b',m[1]) SHADER.blockSatur:send('k',m[2]) - m=saturateValues[SETTING.fieldSatur]or saturateValues.normal + m=saturateValues[SETTING.fieldSatur] or saturateValues.normal SHADER.fieldSatur:send('b',m[1]) SHADER.fieldSatur:send('k',m[2]) - --Apply BG - if SETTING.bg=='on'then + -- Apply BG + if reason=='fullscreen' then return end + if SETTING.bg=='on' then BG.unlock() + BG.setDefault(SETTING.defaultBG) BG.set() - elseif SETTING.bg=='off'then + if SETTING.lockBG then + BG.lock() + elseif reason=='lockBG' then -- We only reload theme again when at settings scene. + THEME.set(THEME.calculate(),GAME.playing) + end + elseif SETTING.bg=='off' then BG.unlock() BG.set('fixColor',SETTING.bgAlpha,SETTING.bgAlpha,SETTING.bgAlpha) BG.lock() - elseif SETTING.bg=='custom'then - if love.filesystem.getInfo('conf/customBG')then - local res,image=pcall(gc.newImage,love.filesystem.newFile('conf/customBG')) + elseif SETTING.bg=='custom' then + if love.filesystem.getInfo('conf/customBG') then + local res,image=pcall(GC.newImage,love.filesystem.newFile('conf/customBG')) if res then BG.unlock() - gc.setDefaultFilter('linear','linear') + GC.setDefaultFilter('linear','linear') BG.set('custom',SETTING.bgAlpha,image) - gc.setDefaultFilter('nearest','nearest') + GC.setDefaultFilter('nearest','nearest') BG.lock() else MES.new('error',text.customBGloadFailed) end - else--Switch off when custom BG not found + else-- Switch off when custom BG not found SETTING.bg='off' BG.unlock() BG.set('fixColor',SETTING.bgAlpha,SETTING.bgAlpha,SETTING.bgAlpha) @@ -189,14 +193,90 @@ do--function applySettings() end end ---Royale mode -function randomTarget(P)--Return a random opponent for P - if #PLY_ALIVE>1 then - local R - repeat - R=PLY_ALIVE[rnd(#PLY_ALIVE)] - until R~=P - return R +------------------------------[Generate Grades]------------------------------ +local smallDigits={[0]="₀","₁","₂","₃","₄","₅","₆","₇","₈","₉"} +local function getSmallNum(num) + local str=tostring(num) + local out="" + for i=1,#str do + out=out..smallDigits[tonumber(str:sub(i,i))] + end + return out +end +do -- Secret Grade + local r={"GM","GM+","TM","TM+"} + function getConstructGrade(index) + if index<11 then -- rank 10 - 1 + return tostring(11-index) + elseif index<20 then -- S1 - S9 ranks + return "S"..index-10 + elseif index<24 then -- GM, GM+, TM, TM+ ranks + return r[index-19] + else + return "TM+"..getSmallNum(index-22) + end + end +end +function getConstructGradeText(index) + if index<11 then + return "Grade "..tostring(11-index) + else + return getConstructGrade(index) + end +end + +do -- Master GRADED + local master_postm_grades={"M","MK","MV","MO","MM-","MM","MM+","GM-","GM","GM+","TM-","TM","TM+"} + function getMasterGrade(index) + if index<10 then + return tostring(10-index) + elseif index<19 then + return "S"..index-9 + elseif index<28 then + return "m"..index-18 + elseif index<41 then + return master_postm_grades[index-27] + else + return master_postm_grades[#master_postm_grades]..getSmallNum(index-39) + end + end + local master_postm_grades_text={ + "Master","MasterK","MasterV","MasterO","MasterM-","MasterM","MasterM+", + "Grand Master-","Grand Master","Grand Master+", + "Tech Master-","Tech Master","Tech Master+" + } + function getMasterGradeText(index) + if index<10 then + return "Grade "..tostring(10-index) + elseif index<19 then + return "S"..index-9 + elseif index<28 then + return "m"..index-18 + elseif index<41 then + return master_postm_grades_text[index-27] + else + return master_postm_grades_text[#master_postm_grades]..index-39 + end + end +end +------------------------------[Royale mode]------------------------------ +function randomTarget(P)-- Return a random opponent for P + local l=TABLE.shift(PLY_ALIVE,0) + local count=0 + for i=1,#l do + if P.group==0 and l[i]~=P or P.group~=l[i].group then + count=count+1 + end + end + if count==0 then return end + count=rnd(count) + for i=1,#l do + if P.group==0 and l[i]~=P or P.group~=l[i].group then + count=count-1 + if count==0 then + return l[i] + end + end end end function freshMostDangerous() @@ -243,7 +323,7 @@ end function royaleLevelup() GAME.stage=GAME.stage+1 local spd - TEXT.show(text.royale_remain:gsub("$1",#PLY_ALIVE),640,200,40,'beat',.3) + TEXT.show(text.royale_remain:repD(#PLY_ALIVE),640,200,40,'beat',.3) if GAME.stage==2 then spd=30 elseif GAME.stage==3 then @@ -273,10 +353,10 @@ function royaleLevelup() for _,P in next,PLY_ALIVE do P.gameEnv.drop=spd end - if GAME.curMode.name:find("_u")then + if GAME.curMode.name:find("_u") then for i=1,#PLY_ALIVE do local P=PLY_ALIVE[i] - P.gameEnv.drop=int(P.gameEnv.drop*.4) + P.gameEnv.drop=floor(P.gameEnv.drop*.4) if P.gameEnv.drop==0 then P.curY=P.ghoY P:set20G(true) @@ -285,29 +365,29 @@ function royaleLevelup() end end ---Sound shortcuts +------------------------------[Sound shortcuts]------------------------------ function playClearSFX(cc) if cc<=0 or cc%1~=0 then return end if cc<=4 then - playSFX('clear_'..cc) + SFX.play('clear_'..cc) elseif cc<=6 then - playSFX('clear_4') + SFX.play('clear_4') elseif cc<=12 then - playSFX('clear_4',.8) + SFX.play('clear_4',.8) if cc<=9 then Snd('bass','A3','E4') else Snd('bass','A3','E4','A4') end elseif cc<=16 then - playSFX('clear_5',.7) + SFX.play('clear_5',.7) if cc<=14 then Snd('bass',.8,'A3','E4')Snd('lead','A4','E5') else Snd('bass',.8,'A3','G4')Snd('lead','B4','G5') end else - playSFX('clear_6',.6) + SFX.play('clear_6',.6) if cc==17 then Snd('bass',.8,'A3','A4')Snd('lead','E5','G5') elseif cc==18 then Snd('bass',.7,'A4')Snd('lead',.8,'C4','G5')Snd('bell','D5') elseif cc==19 then Snd('bass',.7,'A4')Snd('lead',.8,'A4','E5')Snd('bell','B5') @@ -337,7 +417,7 @@ function playReadySFX(i,vol) end ---Game +------------------------------[Game]------------------------------ function getItem(itemName,amount) STAT.item[itemName]=STAT.item[itemName]+(amount or 1) end @@ -351,11 +431,10 @@ function notEmptyLine(L) end end end -function setField(P,page) - local F=FIELD[page] +function setField(P,F) local height=0 for y=#F,1,-1 do - if notEmptyLine(F[y])then + if notEmptyLine(F[y]) then height=y break end @@ -382,16 +461,16 @@ function freshDate(args) STAT.date=date STAT.todayTime=0 getItem('zTicket',1) - if not args:find'q'then + if not args:find'q' then MES.new('info',text.newDay) end saveStats() return true end end -function legalGameTime()--Check if today's playtime is legal +function legalGameTime()-- Check if today's playtime is legal if - SETTING.locale:find'zh'and + SETTING.locale:find'zh' and RANKS.sprint_10l<4 and (not RANKS.sprint_40l or RANKS.sprint_40l<3) then @@ -407,7 +486,7 @@ function legalGameTime()--Check if today's playtime is legal end return true end -do--function trySettingWarn() +do-- function trySettingWarn() local lastWarnTime=0 function trySettingWarn() if TIME()-lastWarnTime>2.6 then @@ -417,22 +496,22 @@ do--function trySettingWarn() end end -function mergeStat(stat,delta)--Merge delta stat. to global stat. +function mergeStat(stat,delta)-- Merge delta stat. to global stat. for k,v in next,delta do - if type(v)=='table'then - if type(stat[k])=='table'then + if type(v)=='table' then + if type(stat[k])=='table' then mergeStat(stat[k],v) end else - if stat[k]then + if stat[k] then stat[k]=stat[k]+v end end end end -function scoreValid()--Check if any unranked mods are activated - for _,M in next,GAME.mod do - if M.unranked then +function scoreValid()-- Check if any unranked mods are activated + for number,sel in next,GAME.mod do + if sel>0 and MODOPT[number].unranked then return false end end @@ -441,13 +520,13 @@ function scoreValid()--Check if any unranked mods are activated end return true end -function destroyPlayers()--Destroy all player objects, restore freerows and free CCs +function destroyPlayers()-- Destroy all player objects, restore freerows and free CCs for i=#PLAYERS,1,-1 do local P=PLAYERS[i] if P.canvas then P.canvas:release() end - while P.field[1]do + while P.field[1] do rem(P.field) rem(P.visTime) end @@ -461,7 +540,7 @@ function pauseGame() for i=1,#PLAYERS do local l=PLAYERS[i].keyPressing for j=1,#l do - if l[j]then + if l[j] then PLAYERS[i]:releaseKey(j) end end @@ -470,31 +549,31 @@ function pauseGame() for i=1,20 do VK.release(i) end - if not(GAME.result or GAME.replaying)then + if not (GAME.result or GAME.replaying) then GAME.pauseCount=GAME.pauseCount+1 end SCN.swapTo('pause','none') end end -function applyCustomGame()--Apply CUSTOMENV, BAG, MISSION +function applyCustomGame()-- Apply CUSTOMENV, BAG, MISSION for k,v in next,CUSTOMENV do GAME.modeEnv[k]=v end - if BAG[1]then + if BAG[1] then GAME.modeEnv.seqData=BAG else GAME.modeEnv.seqData=nil end - if MISSION[1]then + if MISSION[1] then GAME.modeEnv.mission=MISSION else GAME.modeEnv.mission=nil end end -function loadGame(mode,ifQuickPlay,ifNet)--Load a mode and go to game scene +function loadGame(mode,ifQuickPlay,ifNet)-- Load a mode and go to game scene freshDate() - if legalGameTime()then - if not MODES[mode]and love.filesystem.getRealDirectory('parts/modes/'..mode)~=SAVEDIR then + if legalGameTime() then + if not MODES[mode] and FILE.isSafe('parts/modes/'..mode) then MODES[mode]=require('parts.modes.'..mode) MODES[mode].name=mode end @@ -511,14 +590,14 @@ function loadGame(mode,ifQuickPlay,ifNet)--Load a mode and go to game scene if ifNet then SCN.go('net_game','swipeD') else - local modeText=text.modes[mode]or{"["..MODES[mode].name.."]",""} + local modeText=text.modes[mode] or{"["..MODES[mode].name.."]",""} TEXTOBJ.modeName:set(modeText[1].." "..modeText[2]) - SCN.go('game',ifQuickPlay and'swipeD'or'fade_togame') - playSFX('enter') + SCN.go('game',ifQuickPlay and 'swipeD' or 'fade_togame') + SFX.play('enter') end end end -function gameOver()--Save record +function gameOver()-- Save record if GAME.replaying then local R=GAME.curMode.getRank if R then @@ -534,15 +613,15 @@ function gameOver()--Save record local R=M.getRank if R then local P=PLAYERS[1] - R=R(P)--New rank + R=R(P)-- New rank if R then if R>0 then GAME.rank=R end - if not GAME.replaying and M.score and scoreValid()then - if RANKS[M.name]then--Old rank exist + if not GAME.replaying and M.score and scoreValid() then + if RANKS[M.name] then-- Old rank exist local needSave - if R>RANKS[M.name]then + if R>RANKS[M.name] then RANKS[M.name]=R needSave=true end @@ -551,7 +630,7 @@ function gameOver()--Save record for i=1,#M.unlock do local m=M.unlock[i] local n=MODES[m].name - if not RANKS[n]then + if not RANKS[n] then if MODES[m].x then RANKS[n]=0 end @@ -566,9 +645,9 @@ function gameOver()--Save record end local D=M.score(P) local L=M.records - local p=#L--Rank-1 + local p=#L-- Rank-1 if p>0 then - while M.comp(D,L[p])do--If higher rank + while M.comp(D,L[p]) do-- If higher rank p=p-1 if p==0 then break end end @@ -576,15 +655,15 @@ function gameOver()--Save record if p<10 then if p==0 then P:_showText(text.newRecord,0,-100,100,'beat',.5) - if SETTING.autoSave and DATA.saveReplay()then + if SETTING.autoSave and DATA.saveReplay() then GAME.saved=true - playSFX('connected') + SFX.play('connected') MES.new('check',text.saveDone) end end D.date=os.date("%Y/%m/%d %H:%M") ins(L,p+1,D) - if L[11]then L[11]=nil end + if L[11] then L[11]=nil end saveFile(L,('record/%s.rec'):format(M.name),'-luaon') end end @@ -592,7 +671,7 @@ function gameOver()--Save record end end function trySave() - if not GAME.statSaved and PLAYERS[1]and PLAYERS[1].type=='human'and(PLAYERS[1].frameRun>300 or GAME.result)then + if not GAME.statSaved and PLAYERS[1] and PLAYERS[1].type=='human' and (PLAYERS[1].frameRun>300 or GAME.result) then GAME.statSaved=true STAT.game=STAT.game+1 mergeStat(STAT,PLAYERS[1].stat) @@ -600,86 +679,314 @@ function trySave() saveStats() end end -do--function freshPlayerPosition(sudden) - local posLists={ - --1~5 - { - {340,75,1}, - {965,390,.5}, - {965,30,.5}, - {20,390,.5}, - {20,30,.5}, +do-- function freshPlayerPosition(sudden) + local posLists=setmetatable({ + alive={ + [1]={main={340,75,1}}, + [3]={main={340,75,1}, + {25,210,.5}, + {955,210,.5}, + }, + [4]={main={340,75,1}, + {25,210,.5}, + {970,90,.45},{970,410,.45}, + }, + [5]={main={340,75,1}, + {40,90,.45},{40,410,.45}, + {970,90,.45},{970,410,.45}, + }, + [6]={main={340,75,1}, + {40,90,.45},{40,410,.45}, + {1010,80,.305},{1010,290,.305},{1010,500,.305}, + }, + [7]={main={340,75,1}, + {100,80,.305},{100,290,.305},{100,500,.305}, + {1010,80,.305},{1010,290,.305},{1010,500,.305}, + }, + [10]={main={340,75,1}, + {100,80,.305},{100,290,.305},{100,500,.305}, + {935,90,.275},{935,300,.275},{935,510,.275}, + {1105,90,.275},{1105,300,.275},{1105,510,.275}, + }, + [13]={main={340,75,1}, + {10,90,.275},{10,300,.275},{10,510,.275}, + {180,90,.275},{180,300,.275},{180,510,.275}, + {935,90,.275},{935,300,.275},{935,510,.275}, + {1105,90,.275},{1105,300,.275},{1105,510,.275}, + }, + [14]={main={340,75,1}, + {10,90,.275},{10,300,.275},{10,510,.275}, + {180,90,.275},{180,300,.275},{180,510,.275}, + {935,90,.275},{935,300,.275},{935,510,.275}, + {1120,80,.225},{1120,240,.225},{1120,400,.225},{1120,560,.225}, + }, + [15]={main={340,75,1}, + {10,90,.275},{10,300,.275},{10,510,.275}, + {180,90,.275},{180,300,.275},{180,510,.275}, + {960,80,.225},{960,240,.225},{960,400,.225},{960,560,.225}, + {1120,80,.225},{1120,240,.225},{1120,400,.225},{1120,560,.225}, + }, + [16]={main={340,75,1}, + {10,90,.275},{10,300,.275},{10,510,.275}, + {190,80,.225},{190,240,.225},{190,400,.225},{190,560,.225}, + {960,80,.225},{960,240,.225},{960,400,.225},{960,560,.225}, + {1120,80,.225},{1120,240,.225},{1120,400,.225},{1120,560,.225}, + }, + [17]={main={340,75,1}, + {30,80,.225},{30,240,.225},{30,400,.225},{30,560,.225}, + {190,80,.225},{190,240,.225},{190,400,.225},{190,560,.225}, + {960,80,.225},{960,240,.225},{960,400,.225},{960,560,.225}, + {1120,80,.225},{1120,240,.225},{1120,400,.225},{1120,560,.225}, + }, + [24]={main={340,75,1}, + {30,80,.225},{30,240,.225},{30,400,.225},{30,560,.225}, + {190,80,.225},{190,240,.225},{190,400,.225},{190,560,.225}, + {940,80,.175},{940,205,.175},{940,330,.175},{940,455,.175},{940,580,.175}, + {1050,80,.175},{1050,205,.175},{1050,330,.175},{1050,455,.175},{1050,580,.175}, + {1160,80,.175},{1160,205,.175},{1160,330,.175},{1160,455,.175},{1160,580,.175}, + }, + [31]={main={340,75,1}, + {10,80,.175},{10,205,.175},{10,330,.175},{10,455,.175},{10,580,.175}, + {120,80,.175},{120,205,.175},{120,330,.175},{120,455,.175},{120,580,.175}, + {230,80,.175},{230,205,.175},{230,330,.175},{230,455,.175},{230,580,.175}, + {940,80,.175},{940,205,.175},{940,330,.175},{940,455,.175},{940,580,.175}, + {1050,80,.175},{1050,205,.175},{1050,330,.175},{1050,455,.175},{1050,580,.175}, + {1160,80,.175},{1160,205,.175},{1160,330,.175},{1160,455,.175},{1160,580,.175}, + }, + [33]=(function() + local l={main={340,75,1}} + for y=-1.5,1.5 do for x=0,3 do + table.insert(l,{265-85*x,310+160*y,.125}) + table.insert(l,{940+85*x,310+160*y,.125}) + end end + return l + end)(), + [51]=(function() + local l={main={340,75,1}} + for y=-2,2 do for x=0,4 do + table.insert(l,{275-65*x,315+125*y,.1}) + table.insert(l,{945+65*x,315+125*y,.1}) + end end + return l + end)(), + [75]=(function() + local l={main={340,75,1}} + for y=-2,2 do for x=0,4 do + table.insert(l,{275-65*x,310+125*y,.1}) + end end + for y=-3,3 do for x=0,6 do + table.insert(l,{940+47*x,340+92*y,.075}) + end end + return l + end)(), + [99]=(function() + local l={main={340,75,1}} + for y=-3,3 do for x=0,6 do + table.insert(l,{290-47*x,340+92*y,.075}) + table.insert(l,{940+47*x,340+92*y,.075}) + end end + return l + end)(), + [MATH.inf]={main={340,75,1}}, }, - --6~17 - (function() - local L={{340,75,1}} - for i=1,4 do ins(L,{15,-160+180*i,.25})end - for i=1,4 do ins(L,{180,-160+180*i,.25})end - for i=1,4 do ins(L,{950,-160+180*i,.25})end - for i=1,4 do ins(L,{1120,-160+180*i,.25})end - return L - end)(), - --18~31 - (function() - local L={{340,75,1}} - for i=1,5 do ins(L,{10, -100+135*i,.18})end - for i=1,5 do ins(L,{120, -100+135*i,.18})end - for i=1,5 do ins(L,{230, -100+135*i,.18})end - for i=1,5 do ins(L,{940, -100+135*i,.18})end - for i=1,5 do ins(L,{1050,-100+135*i,.18})end - for i=1,5 do ins(L,{1160,-100+135*i,.18})end - return L - end)(), - --32~49 - (function() - local L={{340,75,1}} - for i=1,4 do for j=1,6 do ins(L,{78*i-54,115*j-98,.09})end end - for i=9,12 do for j=1,6 do ins(L,{78*i+267,115*j-98,.09})end end - return L - end)(), - --50~99 - (function() - local L={{340,75,1}} - for i=1,7 do for j=1,7 do ins(L,{46*i-36,97*j-72,.068})end end - for i=15,21 do for j=1,7 do ins(L,{46*i+264,97*j-72,.068})end end - return L - end)(), - } - function freshPlayerPosition(sudden)--Set initial position for every player + dead={ + [1]={{340,75,1}}, + [2]={ + {50,130,.925},{670,130,.925}, + }, + [3]={ + {25,160,.675},{440,160,.675},{855,160,.675}, + }, + [4]={ + {13,200,.525},{328,200,.525},{643,200,.525},{948,200,.525}, + }, + [5]={ + {8,230,.425},{260,230,.425},{512,230,.425},{764,230,.425},{1016,230,.425}, + }, + [10]={ + {8,110,.425},{260,110,.425},{512,110,.425},{764,110,.425},{1016,110,.425}, + {8,410,.425},{260,410,.425},{512,410,.425},{764,410,.425},{1016,410,.425}, + }, + [12]={ + {10,120,.35},{220,120,.35},{430,120,.35},{640,120,.35},{850,120,.35},{1060,120,.35}, + {10,400,.35},{220,400,.35},{430,400,.35},{640,400,.35},{850,400,.35},{1060,400,.35}, + }, + [18]={ + {10,90,.305},{220,90,.305},{430,90,.305},{640,90,.305},{850,90,.305},{1060,90,.305}, + {10,300,.305},{220,300,.305},{430,300,.305},{640,300,.305},{850,300,.305},{1060,300,.305}, + {10,510,.305},{220,510,.305},{430,510,.305},{640,510,.305},{850,510,.305},{1060,510,.305}, + }, + [21]={ + {10,90,.295},{190,90,.295},{370,90,.295},{550,90,.295},{730,90,.295},{910,90,.295},{1090,90,.295}, + {10,300,.295},{190,300,.295},{370,300,.295},{550,300,.295},{730,300,.295},{910,300,.295},{1090,300,.295}, + {10,510,.295},{190,510,.295},{370,510,.295},{550,510,.295},{730,510,.295},{910,510,.295},{1090,510,.295}, + }, + [24]={ + {20,100,.25},{175,100,.25},{330,100,.25},{485,100,.25},{640,100,.25},{795,100,.25},{950,100,.25},{1105,100,.25}, + {20,300,.25},{175,300,.25},{330,300,.25},{485,300,.25},{640,300,.25},{795,300,.25},{950,300,.25},{1105,300,.25}, + {20,500,.25},{175,500,.25},{330,500,.25},{485,500,.25},{640,500,.25},{795,500,.25},{950,500,.25},{1105,500,.25}, + }, + [27]={ + {10,100,.225},{150,100,.225},{290,100,.225},{430,100,.225},{570,100,.225},{710,100,.225},{850,100,.225},{990,100,.225},{1130,100,.225}, + {10,300,.225},{150,300,.225},{290,300,.225},{430,300,.225},{570,300,.225},{710,300,.225},{850,300,.225},{990,300,.225},{1130,300,.225}, + {10,500,.225},{150,500,.225},{290,500,.225},{430,500,.225},{570,500,.225},{710,500,.225},{850,500,.225},{990,500,.225},{1130,500,.225}, + }, + [36]={ + {10,90,.225},{150,90,.225},{290,90,.225},{430,90,.225},{570,90,.225},{710,90,.225},{850,90,.225},{990,90,.225},{1130,90,.225}, + {10,245,.225},{150,245,.225},{290,245,.225},{430,245,.225},{570,245,.225},{710,245,.225},{850,245,.225},{990,245,.225},{1130,245,.225}, + {10,400,.225},{150,400,.225},{290,400,.225},{430,400,.225},{570,400,.225},{710,400,.225},{850,400,.225},{990,400,.225},{1130,400,.225}, + {10,555,.225},{150,555,.225},{290,555,.225},{430,555,.225},{570,555,.225},{710,555,.225},{850,555,.225},{990,555,.225},{1130,555,.225}, + }, + [39]=(function() + local l={} + for y=0,2 do for x=0,12 do + table.insert(l,{13+97*x,110+190*y,.15}) + end end + return l + end)(), + [42]=(function() + local l={} + for y=0,2 do for x=0,13 do + table.insert(l,{15+90*x,120+190*y,.135}) + end end + return l + end)(), + [45]=(function() + local l={} + for y=0,2 do for x=0,14 do + table.insert(l,{8+85*x,120+190*y,.125}) + end end + return l + end)(), + [60]=(function() + local l={} + for y=0,3 do for x=0,14 do + table.insert(l,{8+85*x,85+155*y,.125}) + end end + return l + end)(), + [64]=(function() + local l={} + for y=0,3 do for x=0,15 do + table.insert(l,{13+79*x,85+155*y,.115}) + end end + return l + end)(), + [68]=(function() + local l={} + for y=0,3 do for x=0,16 do + table.insert(l,{6+75*x,85+155*y,.115}) + end end + return l + end)(), + [72]=(function() + local l={} + for y=0,3 do for x=0,17 do + table.insert(l,{15+70*x,95+155*y,.1}) + end end + return l + end)(), + [90]=(function() + local l={} + for y=0,4 do for x=0,17 do + table.insert(l,{15+70*x,82+127*y,.1}) + end end + return l + end)(), + [95]=(function() + local l={} + for y=0,4 do for x=0,18 do + table.insert(l,{15+66*x,82+127*y,.1}) + end end + return l + end)(), + [100]=(function() + local l={} + for y=0,4 do for x=0,19 do + table.insert(l,{12+63*x,82+127*y,.1}) + end end + return l + end)(), + [MATH.inf]={}, + }, + }, { + __call=function(self,alive,count) + local lastTested=MATH.inf + for k in next,self[alive and 'alive' or 'dead'] do + if k=count then + lastTested=k + end + end + return self[alive and 'alive' or 'dead'][lastTested] + end, + }) + + function freshPlayerPosition(mode)-- Set initial position for every player, mode: 'normal'|'quick'|'update' + assert(mode=='normal' or mode=='quick' or mode=='update',"Wrong freshPlyPos mode") local L=PLY_ALIVE - if not sudden then + if mode~='update' then for i=1,#L do L[i]:setPosition(640,#L<=5 and 360 or -62,0) end end - local posList - if #L<=5 then posList=posLists[1] - elseif #L<=17 then posList=posLists[2] - elseif #L<=31 then posList=posLists[3] - elseif #L<=49 then posList=posLists[4] - elseif #L<=99 then posList=posLists[5] - else error("TOO MANY PLAYERS!") + local alive=PLAYERS[1].alive + + if mode=='update' then + if alive then + if #L<=31 then + for i=2,#L do + L[i].miniMode=false + L[i].draw=require"parts.player.draw".norm + end + end + else + if #L<=36 then + for i=1,#L do + L[i].miniMode=false + L[i].draw=require"parts.player.draw".norm + end + end + end + end + + local posList=posLists(alive,#L) + local method=mode=='quick' and 'setPosition' or 'movePosition' + + if alive then + for i=1,#L do + if i==1 then + if SETTING.portrait then-- WARNING: Brutly scaling up to 2x only for 1P, will cause many other visual issues. + L[i][method](L[i],36,-260,2) + else + L[i][method](L[i],unpack(posList['main'])) + end + else + L[i][method](L[i],unpack(posList[i-1])) + end + end + else + for i=1,#L do + L[i][method](L[i],unpack(posList[i])) + end end - local method=sudden and'setPosition'or'movePosition' - for i=1,#L do L[i][method](L[i],unpack(posList[i]))end end end -do--function dumpBasicConfig() +do-- function dumpBasicConfig() local gameSetting={ - --Tuning - 'das','arr','dascut','dropcut','sddas','sdarr', + -- Tuning + 'das','arr','dascut','irscut','dropcut','sddas','sdarr', 'ihs','irs','ims','RS', - --System + -- System 'skin','face', - --Graphic + -- Graphic 'ghostType','block','ghost','center','bagLine', 'dropFX','moveFX','shakeFX', 'text','highCam','nextPos', - --Unnecessary graphic + -- Unnecessary graphic -- 'grid','smooth', -- 'lockFX','clearFX','splashFX','atkFX', -- 'score', @@ -692,31 +999,35 @@ do--function dumpBasicConfig() return JSON.encode(S) end end -do--function resetGameData(args) - local function task_showMods() - local time=0 - while true do - YIELD() - time=time+1 - if time%20==0 then - local M=GAME.mod[time/20] - if M then - TEXT.show(M.id,700+(time-20)%120*4,36,45,'spin',.5) +do-- function resetGameData(args) + local function task_showMods() -- TODO + coroutine.yield() + local counter=0 + for number,sel in next,GAME.mod do + if sel>0 then + if counter==0 then + coroutine.yield() else - return + for _=1,20 do + coroutine.yield() + end end + local M=MODOPT[number] + SFX.play('collect',.2) + TEXT.show(M.id,640+(counter%5-2)*80,26,45,'spin') + counter=counter+1 end end end local gameSetting={ - --Tuning - 'das','arr','dascut','dropcut','sddas','sdarr', + -- Tuning + 'das','arr','dascut','irscut','dropcut','sddas','sdarr', 'ihs','irs','ims','RS', - --System + -- System 'skin','face', - --Graphic + -- Graphic 'block','ghost','center','smooth','grid','bagLine', 'lockFX','dropFX','moveFX','clearFX','splashFX','shakeFX','atkFX', 'text','score','warn','highCam','nextPos', @@ -724,7 +1035,7 @@ do--function resetGameData(args) local function _copyGameSetting() local S={} for _,key in next,gameSetting do - if type(SETTING[key])=='table'then + if type(SETTING[key])=='table' then S[key]=TABLE.shift(SETTING[key]) else S[key]=SETTING[key] @@ -733,22 +1044,22 @@ do--function resetGameData(args) return S end function resetGameData(args,seed) - if not args then args=""end + if not args then args="" end trySave() GAME.result=false GAME.rank=0 GAME.warnLVL0=0 GAME.warnLVL=0 - if args:find'r'then + GAME.pauseCount=0 + GAME.pauseTime=0 + if args:find'r' then GAME.frameStart=0 GAME.recording=false GAME.replaying=true else - GAME.frameStart=args:find'n'and 0 or 180-SETTING.reTime*60 - GAME.seed=seed or math.random(1046101471,2662622626) - GAME.pauseTime=0 - GAME.pauseCount=0 + GAME.frameStart=args:find'n' and 0 or 180-SETTING.reTime*60 + GAME.seed=seed or math.random(1046101471) GAME.saved=false GAME.setting=_copyGameSetting() GAME.tasUsed=false @@ -765,16 +1076,17 @@ do--function resetGameData(args) else PLY.newPlayer(1) end - freshPlayerPosition(args:find'q') + GAME.initPlayerCount=#PLAYERS + freshPlayerPosition((args:find'q') and 'quick' or 'normal') VK.restore() local bg=GAME.modeEnv.bg - BG.set(type(bg)=='string'and bg or type(bg)=='table'and bg[math.random(#bg)]) + BG.set(type(bg)=='string' and bg or type(bg)=='table' and bg[math.random(#bg)]) local bgm=GAME.modeEnv.bgm - BGM.play(type(bgm)=='string'and bgm or type(bgm)=='table'and bgm[math.random(#bgm)]) + BGM.play(type(bgm)=='string' and bgm or type(bgm)=='table' and bgm[math.random(#bgm)]) TEXT.clear() - if GAME.modeEnv.eventSet=='royale'then + if GAME.modeEnv.eventSet=='royale' then for i=1,#PLAYERS do PLAYERS[i]:changeAtk(randomTarget(PLAYERS[i])) end @@ -786,21 +1098,20 @@ do--function resetGameData(args) GAME.stage=1 end TASK.removeTask_code(task_showMods) - if GAME.setting.allowMod then + if PLAYERS[1].gameEnv.allowMod then TASK.new(task_showMods) end playReadySFX(3) collectgarbage() end end -do--function checkWarning() +do-- function checkWarning(P,dt) local max=math.max - function checkWarning(dt) - local P1=PLAYERS[1] - if P1.alive then - if P1.frameRun%26==0 then - local F=P1.field - local height=0--Max height of row 4~7 + function checkWarning(P,dt) + if P.alive then + if P.frameRun%26==0 then + local F=P.field + local height=0-- Max height of row 4~7 for x=4,7 do for y=#F,1,-1 do if F[y][x]>0 then @@ -811,7 +1122,7 @@ do--function checkWarning() end end end - GAME.warnLVL0=math.log(height-(P1.gameEnv.fieldH-5)+P1.atkBufferSum*.8) + GAME.warnLVL0=math.log(height-(P.gameEnv.fieldH-5)+P.atkBufferSum*.8) end local _=GAME.warnLVL if _1.126 and P1.frameRun%30==0 then + if GAME.warnLVL>1.126 and P.frameRun%30==0 then SFX.fplay('warn_beep',SETTING.sfx_warn) end elseif GAME.warnLVL>0 then @@ -828,57 +1139,42 @@ do--function checkWarning() end end end +function usingMod() + for _,sel in next,GAME.mod do + if sel>0 then + return true + end + end + return false +end ---Game draw -do--function drawSelfProfile() - local lvColor={COLOR.J,COLOR.A,COLOR.C,COLOR.N,COLOR.S,COLOR.V,COLOR.P,COLOR.M,COLOR.W,COLOR.R,COLOR.O,COLOR.Y} - local lvIcon=setmetatable({},{__index=function(self,lv) - local c=lvColor[int((lv-1)/26)+1]or COLOR.Z - - local img=GC.DO{25,25, - {"clear",0,0,0,0}, - {"setLW",2}, - {"setCL",c[1],c[2],c[3],.6}, - {"fRect",2,2,21,21,2}, - {"setCL",c}, - {"dRect",2,2,21,21,2}, - {"setCL",COLOR.Z}, - {"mText",(lv-1)%26+1,13,-1}, - } - rawset(self,lv,img) - return img - end}) +------------------------------[Graphics]------------------------------ +do-- function drawSelfProfile() local name local textObj,scaleK,width,offY function drawSelfProfile() - local selfAvatar=USERS.getAvatar(USER.uid) gc_push('transform') gc_replaceTransform(SCR.xOy_ur) - --Draw avatar + -- Draw avatar gc_setLineWidth(2) gc_setColor(COLOR.X)gc_rectangle('fill',0,0,-300,80) gc_setColor(1,1,1)gc_rectangle('line',-300,0,300,80,5) gc_rectangle('line',-73,7,66,66,2) - gc_draw(selfAvatar,-72,8,nil,.5) + gc_draw(USERS.getAvatar(USER.uid),-72,8,nil,.5) - --Draw username - if name~=USERS.getUsername(USER.uid)then + -- Draw username + if name~=USERS.getUsername(USER.uid) then name=USERS.getUsername(USER.uid) - textObj=gc.newText(getFont(30),name) + textObj=GC.newText(getFont(30),name) width=textObj:getWidth() scaleK=210/math.max(width,210) offY=textObj:getHeight()/2 end gc_draw(textObj,-82,26,nil,scaleK,nil,width,offY) - --Draw lv. & xp. - gc_draw(lvIcon[USER.lv],-295,50) - gc_line(-270,55,-80,55,-80,70,-270,70) - gc_rectangle('fill',-210,55,150*USER.xp/USER.lv/USER.lv,15) - gc_pop() end end @@ -887,7 +1183,7 @@ function drawOnlinePlayerCount() gc_setColor(1,1,1) gc_push('transform') gc_replaceTransform(SCR.xOy_ur) - gc_printf(("%s: %s/%s/%s"):format(text.onlinePlayerCount,NET.UserCount,NET.PlayCount,NET.StreamCount),-600,80,594,'right') + gc_printf(text.onlinePlayerCount:repD(NET.onlineCount),-600,80,594,'right') gc_pop() end function drawWarning() @@ -901,56 +1197,55 @@ function drawWarning() gc_pop() end end +function setModBackgroundColor() + gc_setColor(.42,.26,.62,.62+.26*math.sin(TIME()*12.6)) +end ---Widget function shortcuts -function backScene()SCN.back()end -do--function goScene(name,style) +------------------------------[Widget function shortcuts]------------------------------ +function backScene() SCN.back() end +do-- function goScene(name,style) local cache={} function goScene(name,style) local hash=style and name..style or name - if not cache[hash]then - cache[hash]=function()SCN.go(name,style)end + if not cache[hash] then + cache[hash]=function() SCN.go(name,style) end end return cache[hash] end end -do--function swapScene(name,style) +do-- function swapScene(name,style) local cache={} function swapScene(name,style) local hash=style and name..style or name - if not cache[hash]then - cache[hash]=function()SCN.swapTo(name,style)end + if not cache[hash] then + cache[hash]=function() SCN.swapTo(name,style) end end return cache[hash] end end -do--function pressKey(k) +do-- function pressKey(k) local cache={} function pressKey(k) - if not cache[k]then - cache[k]=function()love.keypressed(k)end + if not cache[k] then + cache[k]=function() love.keypressed(k) end end return cache[k] end end -do--CUS/SETXXX(k) - local CUSTOMENV=CUSTOMENV +do-- SETXXX(k) & ROOMXXX(k) local warnList={ - 'das','arr','dascut','dropcut','sddas','sdarr', + 'das','arr','dascut','irscut','dropcut','sddas','sdarr', 'ihs','irs','ims','RS', 'frameMul','highCam', 'VKSwitch','VKIcon','VKTrack','VKDodge', 'simpMode', } - function CUSval(k)return function()return CUSTOMENV[k]end end - function ROOMval(k)return function()return ROOMENV[k]end end - function SETval(k)return function()return SETTING[k]end end - function CUSrev(k)return function()CUSTOMENV[k]=not CUSTOMENV[k]end end - function ROOMrev(k)return function()ROOMENV[k]=not ROOMENV[k]end end - function SETrev(k)return function()if TABLE.find(warnList,k)then trySettingWarn()end SETTING[k]=not SETTING[k]end end - function CUSsto(k)return function(i)CUSTOMENV[k]=i end end - function ROOMsto(k)return function(i)ROOMENV[k]=i end end - function SETsto(k)return function(i)if TABLE.find(warnList,k)then trySettingWarn()end SETTING[k]=i end end + function ROOMval(k) return function() return ROOMENV[k] end end + function SETval(k) return function() return SETTING[k] end end + function ROOMrev(k) return function() ROOMENV[k]=not ROOMENV[k] end end + function SETrev(k) return function() if TABLE.find(warnList,k) then trySettingWarn() end SETTING[k]=not SETTING[k] end end + function ROOMsto(k) return function(i) ROOMENV[k]=i end end + function SETsto(k) return function(i) if TABLE.find(warnList,k) then trySettingWarn() end SETTING[k]=i end end end diff --git a/parts/gameTables.lua b/parts/gameTables.lua index 028ce1fae..5b255f2ee 100644 --- a/parts/gameTables.lua +++ b/parts/gameTables.lua @@ -1,4 +1,4 @@ ---Static data tables +-- Static data tables BLOCK_NAMES={ 'Z','S','J','L','T','O','I', 'Z5','S5','P','Q','F','E', @@ -6,13 +6,13 @@ BLOCK_NAMES={ 'J5','L5','R','Y','N','H','I5', 'I3','C','I2','O1' } -BLOCK_CHARS={}for i=1,#BLOCK_NAMES do BLOCK_CHARS[i]=CHAR.mino[BLOCK_NAMES[i]]end +BLOCK_CHARS={} for i=1,#BLOCK_NAMES do BLOCK_CHARS[i]=CHAR.mino[BLOCK_NAMES[i]] end BLOCK_COLORS={ COLOR.R,COLOR.F,COLOR.O,COLOR.Y,COLOR.L,COLOR.J,COLOR.G,COLOR.A, COLOR.C,COLOR.N,COLOR.S,COLOR.B,COLOR.V,COLOR.P,COLOR.M,COLOR.W, COLOR.dH,COLOR.D,COLOR.lY,COLOR.H,COLOR.lH,COLOR.dV,COLOR.dR,COLOR.dG, } -RANK_CHARS={'B','A','S','U','X'}for i=1,#RANK_CHARS do RANK_CHARS[i]=CHAR.icon['rank'..RANK_CHARS[i]]end +RANK_CHARS={'B','A','S','U','X'} for i=1,#RANK_CHARS do RANK_CHARS[i]=CHAR.icon['rank'..RANK_CHARS[i]] end RANK_COLORS={ {.8,.86,.9}, {.6,.9,.7}, @@ -20,7 +20,17 @@ RANK_COLORS={ {1,.5,.4}, {.95,.5,.95}, } -do--SVG_TITLE_FILL, SVG_TITLE_LINE +GROUP_COLORS={ + [0]=COLOR.Z, + [1]={STRING.hexColor'e57373'}, + [2]={STRING.hexColor"4caf50"}, + [3]={STRING.hexColor'5c6bc0'}, + [4]={STRING.hexColor'ffe082'}, + [5]={STRING.hexColor'ba68c8'}, + [6]={STRING.hexColor'80deea'}, +} + +do-- SVG_TITLE_FILL, SVG_TITLE_LINE SVG_TITLE_FILL={ { 0,0, @@ -131,24 +141,24 @@ do--SVG_TITLE_FILL, SVG_TITLE_LINE SVG_TITLE_LINE=TABLE.shift(SVG_TITLE_FILL) SVG_TITLE_LINE[8],SVG_TITLE_LINE[9]={},{} - for j=1,16 do SVG_TITLE_LINE[8][j]=SVG_TITLE_FILL[8][j]end - for j=19,#SVG_TITLE_FILL[8]-2 do SVG_TITLE_LINE[9][j-18]=SVG_TITLE_FILL[8][j]end + for j=1,16 do SVG_TITLE_LINE[8][j]=SVG_TITLE_FILL[8][j] end + for j=19,#SVG_TITLE_FILL[8]-2 do SVG_TITLE_LINE[9][j-18]=SVG_TITLE_FILL[8][j] end end -do--SVG_TITLE_FAN +do-- SVG_TITLE_FAN SVG_TITLE_FAN={} local sin,cos=math.sin,math.cos for i=1,9 do local L=TABLE.copy(SVG_TITLE_LINE[i]) SVG_TITLE_FAN[i]=L for j=1,#L,2 do - local x,y=L[j],L[j+1]--0