├── .clang-format ├── .clang-tidy ├── .gitattributes ├── .github ├── dependabot.yml └── workflows │ ├── compiler-warnings.yml │ ├── release.yml │ ├── static-analysis.yml │ └── tests.yml ├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── LICENSE.txt ├── README.md ├── cmake └── Sanitisers.cmake ├── fuzzing_targets ├── CMakeLists.txt ├── chart_fuzzer.cpp ├── chart_song_fuzzer.cpp ├── midi_fuzzer.cpp └── midi_song_fuzzer.cpp ├── gui ├── json_settings.cpp ├── json_settings.hpp ├── main.cpp ├── mainwindow.cpp ├── mainwindow.hpp └── mainwindow.ui ├── include ├── cimg_wrapper.hpp ├── engine.hpp ├── image.hpp ├── imagebuilder.hpp ├── ini.hpp ├── optimiser.hpp ├── points.hpp ├── processed.hpp ├── settings.hpp ├── songfile.hpp ├── sp.hpp ├── sptimemap.hpp └── stringutil.hpp ├── integration_tests ├── run_integration_tests.py ├── songs │ ├── amen-brother.chart │ ├── bark-at-the-moon.mid │ ├── cheat-on-the-church.mid │ ├── cult-of-personality-gh3.mid │ ├── cups-and-cakes.mid │ ├── eon-break.chart │ ├── epro.mid │ ├── get-clean.mid │ ├── green-grass-and-high-tides.mid │ ├── ignis-fatuus.mid │ ├── lysios.chart │ ├── me-and-u.chart │ ├── mississippi-queen-rb.mid │ ├── ocean.chart │ ├── oh-my-fucking-god.chart │ ├── pepsiman.chart │ ├── raining-blood-sh.mid │ ├── sanae-unending-status-quo.chart │ ├── satch-boogie-live.chart │ ├── soulless.chart │ ├── sweating-bullets.mid │ ├── the-seconds.chart │ ├── timmy.mid │ ├── type-03.chart │ ├── vampire-in-ghost-town.chart │ └── xon-our-kneesx.chart └── tests.db ├── libs └── CImg.h ├── misc ├── How-fast-is-CHOpt.md ├── How-to-read-paths.md ├── more-than-a-feeling.png ├── runtime-histogram.svg └── setlist.ps1 ├── resources ├── CHOpt.sh ├── chopt.exe.manifest ├── choptgui.exe.manifest ├── icon.ico ├── icon.png ├── icon.svg ├── resources.qrc ├── resources.rc └── sprites │ ├── lefty │ ├── circles │ │ ├── 1.png │ │ ├── 10.png │ │ ├── 11.png │ │ ├── 12.png │ │ ├── 13.png │ │ ├── 14.png │ │ ├── 15.png │ │ ├── 16.png │ │ ├── 17.png │ │ ├── 18.png │ │ ├── 19.png │ │ ├── 2.png │ │ ├── 20.png │ │ ├── 21.png │ │ ├── 22.png │ │ ├── 23.png │ │ ├── 24.png │ │ ├── 25.png │ │ ├── 26.png │ │ ├── 27.png │ │ ├── 28.png │ │ ├── 29.png │ │ ├── 3.png │ │ ├── 30.png │ │ ├── 31.png │ │ ├── 32.png │ │ ├── 4.png │ │ ├── 5.png │ │ ├── 6.png │ │ ├── 7.png │ │ ├── 8.png │ │ └── 9.png │ ├── cymbals │ │ ├── 1.png │ │ ├── 16.png │ │ ├── 2.png │ │ ├── 32.png │ │ ├── 4.png │ │ └── 8.png │ ├── drums │ │ ├── 1.png │ │ ├── 16.png │ │ ├── 2.png │ │ ├── 32.png │ │ ├── 4.png │ │ └── 8.png │ ├── ghl │ │ ├── 1.png │ │ ├── 10.png │ │ ├── 11.png │ │ ├── 12.png │ │ ├── 13.png │ │ ├── 14.png │ │ ├── 15.png │ │ ├── 16.png │ │ ├── 17.png │ │ ├── 18.png │ │ ├── 19.png │ │ ├── 2.png │ │ ├── 20.png │ │ ├── 21.png │ │ ├── 22.png │ │ ├── 23.png │ │ ├── 24.png │ │ ├── 25.png │ │ ├── 26.png │ │ ├── 27.png │ │ ├── 28.png │ │ ├── 29.png │ │ ├── 3.png │ │ ├── 30.png │ │ ├── 31.png │ │ ├── 32.png │ │ ├── 33.png │ │ ├── 34.png │ │ ├── 35.png │ │ ├── 36.png │ │ ├── 37.png │ │ ├── 38.png │ │ ├── 39.png │ │ ├── 4.png │ │ ├── 40.png │ │ ├── 41.png │ │ ├── 42.png │ │ ├── 43.png │ │ ├── 44.png │ │ ├── 45.png │ │ ├── 46.png │ │ ├── 47.png │ │ ├── 48.png │ │ ├── 49.png │ │ ├── 5.png │ │ ├── 50.png │ │ ├── 51.png │ │ ├── 52.png │ │ ├── 53.png │ │ ├── 54.png │ │ ├── 55.png │ │ ├── 56.png │ │ ├── 57.png │ │ ├── 58.png │ │ ├── 59.png │ │ ├── 6.png │ │ ├── 60.png │ │ ├── 61.png │ │ ├── 62.png │ │ ├── 63.png │ │ ├── 64.png │ │ ├── 7.png │ │ ├── 8.png │ │ └── 9.png │ └── stars │ │ ├── 1.png │ │ ├── 10.png │ │ ├── 11.png │ │ ├── 12.png │ │ ├── 13.png │ │ ├── 14.png │ │ ├── 15.png │ │ ├── 16.png │ │ ├── 17.png │ │ ├── 18.png │ │ ├── 19.png │ │ ├── 2.png │ │ ├── 20.png │ │ ├── 21.png │ │ ├── 22.png │ │ ├── 23.png │ │ ├── 24.png │ │ ├── 25.png │ │ ├── 26.png │ │ ├── 27.png │ │ ├── 28.png │ │ ├── 29.png │ │ ├── 3.png │ │ ├── 30.png │ │ ├── 31.png │ │ ├── 32.png │ │ ├── 4.png │ │ ├── 5.png │ │ ├── 6.png │ │ ├── 7.png │ │ ├── 8.png │ │ └── 9.png │ └── righty │ ├── circles │ ├── 1.png │ ├── 10.png │ ├── 11.png │ ├── 12.png │ ├── 13.png │ ├── 14.png │ ├── 15.png │ ├── 16.png │ ├── 17.png │ ├── 18.png │ ├── 19.png │ ├── 2.png │ ├── 20.png │ ├── 21.png │ ├── 22.png │ ├── 23.png │ ├── 24.png │ ├── 25.png │ ├── 26.png │ ├── 27.png │ ├── 28.png │ ├── 29.png │ ├── 3.png │ ├── 30.png │ ├── 31.png │ ├── 32.png │ ├── 4.png │ ├── 5.png │ ├── 6.png │ ├── 7.png │ ├── 8.png │ └── 9.png │ ├── cymbals │ ├── 1.png │ ├── 16.png │ ├── 2.png │ ├── 32.png │ ├── 4.png │ └── 8.png │ ├── drums │ ├── 1.png │ ├── 16.png │ ├── 2.png │ ├── 32.png │ ├── 4.png │ └── 8.png │ ├── ghl │ ├── 1.png │ ├── 10.png │ ├── 11.png │ ├── 12.png │ ├── 13.png │ ├── 14.png │ ├── 15.png │ ├── 16.png │ ├── 17.png │ ├── 18.png │ ├── 19.png │ ├── 2.png │ ├── 20.png │ ├── 21.png │ ├── 22.png │ ├── 23.png │ ├── 24.png │ ├── 25.png │ ├── 26.png │ ├── 27.png │ ├── 28.png │ ├── 29.png │ ├── 3.png │ ├── 30.png │ ├── 31.png │ ├── 32.png │ ├── 33.png │ ├── 34.png │ ├── 35.png │ ├── 36.png │ ├── 37.png │ ├── 38.png │ ├── 39.png │ ├── 4.png │ ├── 40.png │ ├── 41.png │ ├── 42.png │ ├── 43.png │ ├── 44.png │ ├── 45.png │ ├── 46.png │ ├── 47.png │ ├── 48.png │ ├── 49.png │ ├── 5.png │ ├── 50.png │ ├── 51.png │ ├── 52.png │ ├── 53.png │ ├── 54.png │ ├── 55.png │ ├── 56.png │ ├── 57.png │ ├── 58.png │ ├── 59.png │ ├── 6.png │ ├── 60.png │ ├── 61.png │ ├── 62.png │ ├── 63.png │ ├── 64.png │ ├── 7.png │ ├── 8.png │ └── 9.png │ └── stars │ ├── 1.png │ ├── 10.png │ ├── 11.png │ ├── 12.png │ ├── 13.png │ ├── 14.png │ ├── 15.png │ ├── 16.png │ ├── 17.png │ ├── 18.png │ ├── 19.png │ ├── 2.png │ ├── 20.png │ ├── 21.png │ ├── 22.png │ ├── 23.png │ ├── 24.png │ ├── 25.png │ ├── 26.png │ ├── 27.png │ ├── 28.png │ ├── 29.png │ ├── 3.png │ ├── 30.png │ ├── 31.png │ ├── 32.png │ ├── 4.png │ ├── 5.png │ ├── 6.png │ ├── 7.png │ ├── 8.png │ └── 9.png ├── setup.cfg ├── src ├── image.cpp ├── imagebuilder.cpp ├── ini.cpp ├── main.cpp ├── optimiser.cpp ├── points.cpp ├── processed.cpp ├── settings.cpp ├── songfile.cpp ├── sp.cpp ├── sptimemap.cpp └── stringutil.cpp └── tests ├── imagebuilder_unittest.cpp ├── ini_unittest.cpp ├── optimiser_unittest.cpp ├── points_unittest.cpp ├── processed_unittest.cpp ├── sp_unittest.cpp ├── stringutil_unittest.cpp ├── test_helpers.hpp └── test_main.cpp /.clang-format: -------------------------------------------------------------------------------- 1 | Language: Cpp 2 | 3 | AccessModifierOffset: -4 4 | AlignAfterOpenBracket: Align 5 | AlignConsecutiveAssignments: false 6 | AlignConsecutiveDeclarations: false 7 | AlignConsecutiveMacros: false 8 | AlignEscapedNewlines: Right 9 | AlignOperands: false 10 | AlignTrailingComments: false 11 | AllowAllArgumentsOnNextLine: true 12 | AllowAllConstructorInitializersOnNextLine: true 13 | AllowAllParametersOfDeclarationOnNextLine: true 14 | AllowShortBlocksOnASingleLine: false 15 | AllowShortCaseLabelsOnASingleLine: false 16 | AllowShortFunctionsOnASingleLine: All 17 | AllowShortLambdasOnASingleLine: All 18 | AllowShortIfStatementsOnASingleLine: Never 19 | AllowShortLoopsOnASingleLine: false 20 | AlwaysBreakAfterDefinitionReturnType: None 21 | AlwaysBreakAfterReturnType: None 22 | AlwaysBreakBeforeMultilineStrings: false 23 | AlwaysBreakTemplateDeclarations: MultiLine 24 | BinPackArguments: true 25 | BinPackParameters: true 26 | BraceWrapping: 27 | AfterCaseLabel: false 28 | AfterClass: false 29 | AfterControlStatement: false 30 | AfterEnum: false 31 | AfterFunction: true 32 | AfterNamespace: false 33 | AfterObjCDeclaration: false 34 | AfterStruct: false 35 | AfterUnion: false 36 | AfterExternBlock: false 37 | BeforeCatch: false 38 | BeforeElse: false 39 | IndentBraces: false 40 | SplitEmptyFunction: true 41 | SplitEmptyRecord: true 42 | SplitEmptyNamespace: true 43 | BreakBeforeBinaryOperators: All 44 | BreakBeforeBraces: WebKit 45 | BreakBeforeInheritanceComma: false 46 | BreakInheritanceList: BeforeColon 47 | BreakBeforeTernaryOperators: true 48 | BreakConstructorInitializersBeforeComma: false 49 | BreakConstructorInitializers: BeforeComma 50 | BreakAfterJavaFieldAnnotations: false 51 | BreakStringLiterals: true 52 | ColumnLimit: 80 53 | CommentPragmas: '^ IWYU pragma:' 54 | CompactNamespaces: false 55 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 56 | ConstructorInitializerIndentWidth: 4 57 | ContinuationIndentWidth: 4 58 | Cpp11BracedListStyle: true 59 | DerivePointerAlignment: false 60 | DisableFormat: false 61 | ExperimentalAutoDetectBinPacking: false 62 | FixNamespaceComments: false 63 | IncludeBlocks: Preserve 64 | IncludeCategories: 65 | - Regex: '^"(llvm|llvm-c|clang|clang-c)/' 66 | Priority: 2 67 | - Regex: '^(<|"(gtest|gmock|isl|json)/)' 68 | Priority: 3 69 | - Regex: '.*' 70 | Priority: 1 71 | IncludeIsMainRegex: '(Test)?$' 72 | IndentCaseLabels: false 73 | IndentPPDirectives: None 74 | IndentWidth: 4 75 | IndentWrappedFunctionNames: false 76 | JavaScriptQuotes: Leave 77 | JavaScriptWrapImports: true 78 | KeepEmptyLinesAtTheStartOfBlocks: true 79 | MacroBlockBegin: '' 80 | MacroBlockEnd: '' 81 | MaxEmptyLinesToKeep: 1 82 | NamespaceIndentation: Inner 83 | ObjCBinPackProtocolList: Auto 84 | ObjCBlockIndentWidth: 4 85 | ObjCSpaceAfterProperty: true 86 | ObjCSpaceBeforeProtocolList: true 87 | PenaltyBreakAssignment: 2 88 | PenaltyBreakBeforeFirstCallParameter: 19 89 | PenaltyBreakComment: 300 90 | PenaltyBreakFirstLessLess: 120 91 | PenaltyBreakString: 1000 92 | PenaltyBreakTemplateDeclaration: 10 93 | PenaltyExcessCharacter: 1000000 94 | PenaltyReturnTypeOnItsOwnLine: 60 95 | PointerAlignment: Left 96 | ReflowComments: true 97 | SortIncludes: true 98 | SortUsingDeclarations: true 99 | SpaceAfterCStyleCast: false 100 | SpaceAfterLogicalNot: false 101 | SpaceAfterTemplateKeyword: true 102 | SpaceBeforeAssignmentOperators: true 103 | SpaceBeforeCpp11BracedList: true 104 | SpaceBeforeCtorInitializerColon: true 105 | SpaceBeforeInheritanceColon: true 106 | SpaceBeforeParens: ControlStatements 107 | SpaceBeforeRangeBasedForLoopColon: true 108 | SpaceInEmptyParentheses: false 109 | SpacesBeforeTrailingComments: 1 110 | SpacesInAngles: false 111 | SpacesInContainerLiterals: true 112 | SpacesInCStyleCastParentheses: false 113 | SpacesInParentheses: false 114 | SpacesInSquareBrackets: false 115 | Standard: Cpp11 116 | TabWidth: 4 117 | UseTab: Never 118 | -------------------------------------------------------------------------------- /.clang-tidy: -------------------------------------------------------------------------------- 1 | Checks: > 2 | boost-*, 3 | bugprone-*, 4 | -bugprone-easily-swappable-parameters, 5 | cert-*, 6 | -clang-analyzer-optin.cplusplus.VirtualCall, 7 | concurrency-*, 8 | cppcoreguidelines-*, 9 | -cppcoreguidelines-pro-type-vararg, 10 | misc-*, 11 | -misc-const-correctness, 12 | -misc-include-cleaner, 13 | -misc-no-recursion, 14 | modernize-*, 15 | -modernize-use-trailing-return-type, 16 | performance-*, 17 | portability-*, 18 | readability-*, 19 | -readability-identifier-length 20 | WarningsAsErrors: '*' 21 | HeaderFilterRegex: '' 22 | FormatStyle: none 23 | CheckOptions: 24 | - key: misc-non-private-member-variables-in-classes.IgnoreClassesWithAllMemberVariablesBeingPublic 25 | value: true 26 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | libs/* linguist-vendored 2 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: "weekly" 7 | -------------------------------------------------------------------------------- /.github/workflows/compiler-warnings.yml: -------------------------------------------------------------------------------- 1 | name: Compiler Warnings 2 | 3 | on: push 4 | 5 | jobs: 6 | msvc: 7 | runs-on: windows-2022 8 | steps: 9 | - name: Checkout 10 | uses: actions/checkout@v4 11 | with: 12 | submodules: true 13 | 14 | - name: Install Qt 15 | uses: jurplel/install-qt-action@v4 16 | with: 17 | version: 6.7.2 18 | 19 | - name: Install other dependencies 20 | run: vcpkg install boost-test libpng 21 | 22 | - name: Make build directory 23 | run: mkdir build 24 | 25 | - name: Generate build files 26 | working-directory: build 27 | env: 28 | CMAKE_GENERATOR: "Visual Studio 17 2022" 29 | CMAKE_TOOLCHAIN_FILE: "C:/vcpkg/scripts/buildsystems/vcpkg.cmake" 30 | run: cmake .. 31 | 32 | - name: Build 33 | working-directory: build 34 | run: cmake --build . --verbose 35 | 36 | apple-clang: 37 | runs-on: macos-14 38 | steps: 39 | - name: Checkout 40 | uses: actions/checkout@v4 41 | with: 42 | submodules: true 43 | 44 | - name: Install Qt 45 | uses: jurplel/install-qt-action@v4 46 | with: 47 | version: 6.7.2 48 | 49 | - name: Install Boost 50 | run: brew install boost 51 | 52 | - name: Make build directory 53 | run: mkdir build 54 | 55 | - name: Generate build files 56 | working-directory: build 57 | run: cmake .. 58 | 59 | - name: Build 60 | working-directory: build 61 | run: cmake --build . --verbose 62 | 63 | linux-gcc: 64 | runs-on: ubuntu-24.04 65 | steps: 66 | - name: Checkout 67 | uses: actions/checkout@v4 68 | with: 69 | submodules: true 70 | 71 | - name: Install libpng 72 | run: | 73 | sudo apt-get update 74 | sudo apt-get install -y libpng-dev 75 | 76 | - name: Install Qt 77 | uses: jurplel/install-qt-action@v4 78 | with: 79 | version: 6.6.* 80 | 81 | - name: Install Boost Test 82 | run: vcpkg install boost-test 83 | 84 | - name: Make build directory 85 | run: mkdir build 86 | 87 | - name: Generate build files 88 | working-directory: build 89 | env: 90 | CMAKE_TOOLCHAIN_FILE: /usr/local/share/vcpkg/scripts/buildsystems/vcpkg.cmake 91 | CXX: g++-14 92 | run: cmake .. 93 | 94 | - name: Build 95 | working-directory: build 96 | run: cmake --build . --verbose 97 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | tags: 6 | - 'v**' 7 | workflow_dispatch: 8 | 9 | jobs: 10 | determine-version: 11 | runs-on: ubuntu-24.04 12 | outputs: 13 | version: ${{ steps.determine-version.outputs.version }} 14 | steps: 15 | - id: determine-version 16 | run: | 17 | if ${{ startsWith(github.ref, 'refs/tags/v') }}; then 18 | echo "version=${GITHUB_REF#refs/tags/v}" >> "$GITHUB_OUTPUT" 19 | else 20 | echo "version=${GITHUB_SHA:0:7}" >> "$GITHUB_OUTPUT" 21 | fi 22 | 23 | windows: 24 | runs-on: windows-2022 25 | needs: determine-version 26 | steps: 27 | - name: Checkout 28 | uses: actions/checkout@v4 29 | with: 30 | submodules: true 31 | 32 | - name: Install Qt 33 | uses: jurplel/install-qt-action@v4 34 | with: 35 | version: 6.7.2 36 | 37 | - name: Install other dependencies 38 | run: vcpkg --triplet x64-windows-static-md install libpng 39 | 40 | - name: Make build directory 41 | run: mkdir build 42 | 43 | - name: Generate build files 44 | working-directory: build 45 | env: 46 | CMAKE_GENERATOR: "Visual Studio 17 2022" 47 | CMAKE_TOOLCHAIN_FILE: "C:/vcpkg/scripts/buildsystems/vcpkg.cmake" 48 | run: cmake -DVCPKG_TARGET_TRIPLET=x64-windows-static-md -DENABLE_LTO=ON -DPACKAGE_TESTS=OFF .. 49 | 50 | - name: Build 51 | working-directory: build 52 | run: cmake --build . --verbose --config Release 53 | 54 | - name: Prepare CLI release 55 | working-directory: build/Release 56 | run: | 57 | mkdir ..\cli-artifacts 58 | Move-Item -Path chopt.exe -Destination ..\cli-artifacts\CHOpt.exe 59 | windeployqt --release --no-network --no-opengl-sw --no-svg --no-system-d3d-compiler --no-translations ..\cli-artifacts\CHOpt.exe 60 | rm -r ..\cli-artifacts\imageformats 61 | rm -r ..\cli-artifacts\platforms 62 | 63 | - name: Prepare GUI release 64 | working-directory: build/Release 65 | run: | 66 | mkdir ..\gui-artifacts 67 | Rename-Item choptgui.exe CHOpt.exe 68 | $env:VCINSTALLDIR = "$env:ProgramFiles\Microsoft Visual Studio\2022\Enterprise\VC" 69 | windeployqt --release --no-network --no-opengl-sw --no-svg --no-system-d3d-compiler --no-translations CHOpt.exe 70 | rm vc_redist.x64.exe 71 | rm -r imageformats 72 | Copy-Item -Path * -Destination ..\gui-artifacts -Recurse 73 | 74 | - name: Upload CLI release 75 | uses: actions/upload-artifact@v4 76 | with: 77 | name: "CHOpt CLI ${{ needs.determine-version.outputs.version }} (x64 Windows)" 78 | path: build/cli-artifacts/**/* 79 | 80 | - name: Upload GUI release 81 | uses: actions/upload-artifact@v4 82 | with: 83 | name: "CHOpt ${{ needs.determine-version.outputs.version }} (x64 Windows)" 84 | path: build/gui-artifacts/**/* 85 | 86 | macos: 87 | runs-on: macos-14 88 | needs: determine-version 89 | steps: 90 | - name: Checkout 91 | uses: actions/checkout@v4 92 | with: 93 | submodules: true 94 | 95 | - name: Install Qt 96 | uses: jurplel/install-qt-action@v4 97 | with: 98 | version: 6.7.2 99 | 100 | - name: Make build directory 101 | run: mkdir build 102 | 103 | - name: Generate build files 104 | working-directory: build 105 | env: 106 | CMAKE_BUILD_TYPE: Release 107 | run: cmake -DENABLE_LTO=ON -DPACKAGE_TESTS=OFF -DCMAKE_FIND_FRAMEWORK=LAST .. 108 | 109 | - name: Build 110 | working-directory: build 111 | run: cmake --build . --verbose 112 | 113 | - name: Prepare CLI release 114 | working-directory: build 115 | run: | 116 | mkdir cli-artifacts 117 | strip -x chopt 118 | install_name_tool -change /opt/homebrew/opt/libpng/lib/libpng16.16.dylib @executable_path/libpng.dylib chopt 119 | install_name_tool -change @rpath/QtCore.framework/Versions/A/QtCore @executable_path/QtCore chopt 120 | install_name_tool -change @rpath/QtGui.framework/Versions/A/QtGui @executable_path/QtGui chopt 121 | mv chopt cli-artifacts/CHOpt 122 | cp /opt/homebrew/lib/libpng.dylib cli-artifacts/libpng.dylib 123 | cp ${QT_ROOT_DIR}/lib/QtCore.framework/Versions/A/QtCore cli-artifacts/QtCore 124 | cp ${QT_ROOT_DIR}/lib/QtGui.framework/Versions/A/QtGui cli-artifacts/QtGui 125 | 126 | - name: Prepare GUI release 127 | working-directory: build 128 | run: | 129 | mkdir gui-artifacts 130 | mv choptgui.app CHOpt.app 131 | mv CHOpt.app/Contents/MacOS/choptgui CHOpt.app/Contents/MacOS/CHOpt 132 | sed -i "" "s/choptgui/CHOpt/g" CHOpt.app/Contents/Info.plist 133 | macdeployqt CHOpt.app 134 | mv CHOpt.app gui-artifacts/CHOpt.app 135 | 136 | - name: Upload CLI release 137 | uses: actions/upload-artifact@v4 138 | with: 139 | name: "CHOpt CLI ${{ needs.determine-version.outputs.version }} (x64 Mac)" 140 | path: build/cli-artifacts/**/* 141 | 142 | - name: Upload GUI release 143 | uses: actions/upload-artifact@v4 144 | with: 145 | name: "CHOpt ${{ needs.determine-version.outputs.version }} (x64 Mac)" 146 | path: build/gui-artifacts/**/* 147 | 148 | linux: 149 | runs-on: ubuntu-24.04 150 | needs: determine-version 151 | steps: 152 | - name: Checkout 153 | uses: actions/checkout@v4 154 | with: 155 | submodules: true 156 | 157 | - name: Install libpng 158 | run: | 159 | sudo apt-get update 160 | sudo apt-get install -y libpng-dev 161 | 162 | - name: Install Qt 163 | uses: jurplel/install-qt-action@v4 164 | with: 165 | version: 6.6.* 166 | 167 | - name: Make build directory 168 | run: mkdir build 169 | 170 | - name: Generate build files 171 | working-directory: build 172 | env: 173 | CMAKE_BUILD_TYPE: Release 174 | CXX: g++-14 175 | run: cmake -DENABLE_LTO=ON -DPACKAGE_TESTS=OFF .. 176 | 177 | - name: Build 178 | working-directory: build 179 | run: cmake --build . --verbose 180 | 181 | - name: Prepare CLI release 182 | working-directory: build 183 | run: | 184 | mkdir cli-artifacts 185 | strip -s chopt 186 | mv chopt cli-artifacts/CHOpt 187 | cp ${QT_ROOT_DIR}/lib/libQt6Core.so.6 cli-artifacts/libQt6Core.so.6 188 | cp ${QT_ROOT_DIR}/lib/libQt6Gui.so.6 cli-artifacts/libQt6Gui.so.6 189 | 190 | - name: Prepare GUI release 191 | working-directory: build 192 | run: | 193 | mkdir -p gui-artifacts/libs 194 | mkdir -p gui-artifacts/platforms 195 | strip -s choptgui 196 | mv choptgui gui-artifacts/CHOpt 197 | cp ../resources/CHOpt.sh gui-artifacts/CHOpt.sh 198 | cp ${QT_ROOT_DIR}/lib/libicudata.so.56 gui-artifacts/libs/libicudata.so.56 199 | cp ${QT_ROOT_DIR}/lib/libicui18n.so.56 gui-artifacts/libs/libicui18n.so.56 200 | cp ${QT_ROOT_DIR}/lib/libicuuc.so.56 gui-artifacts/libs/libicuuc.so.56 201 | cp ${QT_ROOT_DIR}/lib/libQt6Core.so.6 gui-artifacts/libs/libQt6Core.so.6 202 | cp ${QT_ROOT_DIR}/lib/libQt6DBus.so.6 gui-artifacts/libs/libQt6DBus.so.6 203 | cp ${QT_ROOT_DIR}/lib/libQt6Gui.so.6 gui-artifacts/libs/libQt6Gui.so.6 204 | cp ${QT_ROOT_DIR}/lib/libQt6OpenGL.so.6 gui-artifacts/libs/libQt6OpenGL.so.6 205 | cp ${QT_ROOT_DIR}/lib/libQt6Widgets.so.6 gui-artifacts/libs/libQt6Widgets.so.6 206 | cp ${QT_ROOT_DIR}/lib/libQt6XcbQpa.so.6 gui-artifacts/libs/libQt6XcbQpa.so.6 207 | cp ${QT_PLUGIN_PATH}/platforms/libqxcb.so gui-artifacts/platforms/libqxcb.so 208 | 209 | - name: Upload CLI release 210 | uses: actions/upload-artifact@v4 211 | with: 212 | name: "CHOpt CLI ${{ needs.determine-version.outputs.version }} (x64 Linux)" 213 | path: build/cli-artifacts/**/* 214 | 215 | - name: Upload GUI release 216 | uses: actions/upload-artifact@v4 217 | with: 218 | name: "CHOpt ${{ needs.determine-version.outputs.version }} (x64 Linux)" 219 | path: build/gui-artifacts/**/* 220 | -------------------------------------------------------------------------------- /.github/workflows/static-analysis.yml: -------------------------------------------------------------------------------- 1 | name: Static Analysis 2 | 3 | on: push 4 | 5 | jobs: 6 | clang-tidy: 7 | runs-on: ubuntu-24.04 8 | steps: 9 | - name: Checkout 10 | uses: actions/checkout@v4 11 | with: 12 | submodules: true 13 | 14 | - name: Install libpng 15 | run: | 16 | sudo apt-get update 17 | sudo apt-get install -y libpng-dev 18 | 19 | - name: Install Qt 20 | uses: jurplel/install-qt-action@v4 21 | with: 22 | version: 6.6.* 23 | 24 | - name: Install Boost Test 25 | run: vcpkg install boost-test 26 | 27 | - name: Make build directory 28 | run: mkdir build 29 | 30 | - name: Generate build files 31 | working-directory: build 32 | env: 33 | CXX: g++-14 34 | run: cmake -DPACKAGE_TESTS=OFF .. 35 | 36 | - name: Build 37 | working-directory: build 38 | run: cmake --build . --verbose 39 | 40 | - name: Main application files 41 | run: > 42 | clang-tidy-18 src/*.cpp -- -std=c++20 43 | -Iinclude -Ilibs -Iextern/sightread/include 44 | -I${QT_ROOT_DIR}/include -I${QT_ROOT_DIR}/include/QtCore -I${QT_ROOT_DIR}/include/QtGui 45 | 46 | - name: GUI frontend files 47 | run: > 48 | clang-tidy-18 gui/*.cpp -checks=-cppcoreguidelines-owning-memory,-readability-inconsistent-declaration-parameter-name 49 | -- -std=c++20 50 | -Iinclude -Ilibs -Iextern/sightread/include 51 | -I${QT_ROOT_DIR}/include -I${QT_ROOT_DIR}/include/QtCore -I${QT_ROOT_DIR}/include/QtGui -I${QT_ROOT_DIR}/include/QtWidgets 52 | -Ibuild/choptgui_autogen/include 53 | 54 | - name: Test files 55 | run: > 56 | clang-tidy-18 tests/*_unittest.cpp 57 | -checks=-cppcoreguidelines-avoid-do-while,-cppcoreguidelines-avoid-magic-numbers,-cppcoreguidelines-avoid-non-const-global-variables,-cppcoreguidelines-macro-usage,-clang-analyzer-*,-readability-function-cognitive-complexity,-readability-magic-numbers 58 | -- -Iinclude -Iextern/sightread/include -I${QT_ROOT_DIR}/include -I${QT_ROOT_DIR}/include/QtCore 59 | -I ${VCPKG_INSTALLATION_ROOT}/installed/x64-linux/include -std=c++20 60 | 61 | clang-format: 62 | runs-on: ubuntu-24.04 63 | steps: 64 | - name: Checkout 65 | uses: actions/checkout@v4 66 | 67 | - name: Format 68 | run: | 69 | clang-format-18 -i gui/*.hpp gui/*.cpp include/*.hpp src/*.cpp tests/*.cpp tests/*.hpp 70 | git diff --color --exit-code 71 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: push 4 | 5 | jobs: 6 | unit-tests: 7 | runs-on: ubuntu-24.04 8 | steps: 9 | - name: Checkout 10 | uses: actions/checkout@v4 11 | with: 12 | submodules: true 13 | 14 | - name: Install libpng 15 | run: | 16 | sudo apt-get update 17 | sudo apt-get install -y libpng-dev 18 | 19 | - name: Install Qt 20 | uses: jurplel/install-qt-action@v4 21 | with: 22 | version: 6.6.* 23 | 24 | - name: Install Boost Test 25 | run: vcpkg install boost-test 26 | 27 | - name: Make build directory 28 | run: mkdir build 29 | 30 | - name: Generate build files 31 | working-directory: build 32 | env: 33 | CMAKE_TOOLCHAIN_FILE: /usr/local/share/vcpkg/scripts/buildsystems/vcpkg.cmake 34 | CXX: g++-14 35 | run: > 36 | cmake -DBUILD_GUI=OFF -DENABLE_SANITISER_ADDRESS=ON 37 | -DENABLE_SANITISER_LEAK=ON -DENABLE_SANITISER_UNDEFINED_BEHAVIOUR=ON 38 | .. 39 | 40 | - name: Build 41 | working-directory: build 42 | run: cmake --build . --verbose 43 | 44 | - name: Run unit tests 45 | working-directory: build 46 | run: ctest --verbose 47 | 48 | integration-tests: 49 | runs-on: ubuntu-24.04 50 | steps: 51 | - name: Checkout 52 | uses: actions/checkout@v4 53 | with: 54 | submodules: true 55 | 56 | - name: Install libpng 57 | run: | 58 | sudo apt-get update 59 | sudo apt-get install -y libpng-dev 60 | 61 | - name: Install Qt 62 | uses: jurplel/install-qt-action@v4 63 | with: 64 | version: 6.6.* 65 | 66 | - name: Make build directory 67 | run: mkdir build 68 | 69 | - name: Generate build files 70 | working-directory: build 71 | env: 72 | CXX: g++-14 73 | run: > 74 | cmake -DBUILD_GUI=OFF -DPACKAGE_TESTS=OFF 75 | -DENABLE_SANITISER_ADDRESS=ON -DENABLE_SANITISER_LEAK=ON 76 | -DENABLE_SANITISER_UNDEFINED_BEHAVIOUR=ON .. 77 | 78 | - name: Build 79 | working-directory: build 80 | run: cmake --build . --verbose 81 | 82 | - name: Integration tests 83 | run: python3 integration_tests/run_integration_tests.py 84 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vs/ 2 | .vscode/ 3 | build/ 4 | out/ 5 | fuzzing_targets/CHART_CORPUS/ 6 | fuzzing_targets/MIDI_CORPUS/ 7 | chopt-env/ 8 | vcpkg_installed/ 9 | CMakePresets.json 10 | CMakeSettings.json 11 | CMakeUserPresets.json 12 | vcpkg.json 13 | vcpkg-configuration.json 14 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "extern/sightread"] 2 | path = extern/sightread 3 | url = https://github.com//GenericMadScientist/SightRead 4 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.30.0) 2 | 3 | project( 4 | chopt 5 | VERSION 1.9.5 6 | DESCRIPTION "A program to generate optimal Star Power paths for Clone Hero" 7 | LANGUAGES CXX) 8 | 9 | set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake;${CMAKE_MODULE_PATH}") 10 | include(cmake/Sanitisers.cmake) 11 | 12 | # Require standard C++20 13 | function(set_cpp_standard target) 14 | set_target_properties( 15 | ${target} 16 | PROPERTIES CXX_STANDARD 20 17 | CXX_STANDARD_REQUIRED ON 18 | CXX_EXTENSIONS OFF) 19 | endfunction() 20 | 21 | # Set warning flags 22 | function(set_warnings target) 23 | target_compile_options( 24 | ${target} 25 | PRIVATE $<$: 26 | -Wall 27 | -Wextra 28 | -Werror 29 | -Wno-c++98-compat 30 | -Wno-unknown-warning-option> 31 | $<$: 32 | /W4 33 | /WX>) 34 | endfunction() 35 | 36 | find_package(PNG REQUIRED) 37 | find_package( 38 | Qt6 REQUIRED 39 | COMPONENTS Core 40 | OPTIONAL_COMPONENTS Gui Widgets) 41 | 42 | set(CMAKE_AUTOMOC ON) 43 | set(CMAKE_AUTORCC ON) 44 | set(CMAKE_AUTOUIC ON) 45 | set(CMAKE_INCLUDE_CURRENT_DIR ON) 46 | add_subdirectory("extern/sightread") 47 | 48 | include(CheckPIESupported) 49 | check_pie_supported(OUTPUT_VARIABLE output LANGUAGES CXX) 50 | 51 | if(NOT CMAKE_CXX_LINK_NO_PIE_SUPPORTED) 52 | message(VERBOSE "No PIE is not supported at link time: ${output}.\n" 53 | "PIE link options will not be passed to linker.") 54 | endif() 55 | 56 | add_executable( 57 | chopt 58 | src/main.cpp 59 | src/image.cpp 60 | src/imagebuilder.cpp 61 | src/ini.cpp 62 | src/optimiser.cpp 63 | src/points.cpp 64 | src/processed.cpp 65 | src/settings.cpp 66 | src/songfile.cpp 67 | src/sp.cpp 68 | src/sptimemap.cpp 69 | src/stringutil.cpp 70 | resources/chopt.exe.manifest 71 | resources/resources.qrc 72 | resources/resources.rc) 73 | target_include_directories( 74 | chopt PRIVATE "${PROJECT_SOURCE_DIR}/include" "${PROJECT_SOURCE_DIR}/libs" ${PNG_INCLUDE_DIRS}) 75 | target_link_libraries(chopt PRIVATE ${PNG_LIBRARIES} Qt6::Core Qt6::Gui sightread) 76 | 77 | set_property(TARGET chopt PROPERTY POSITION_INDEPENDENT_CODE FALSE) 78 | set_cpp_standard(chopt) 79 | set_warnings(chopt) 80 | enable_sanitisers(chopt) 81 | 82 | option(BUILD_GUI "Build the GUI version" ON) 83 | 84 | if(BUILD_GUI) 85 | add_executable( 86 | choptgui WIN32 87 | gui/json_settings.cpp 88 | gui/main.cpp 89 | gui/mainwindow.cpp 90 | gui/mainwindow.ui 91 | src/image.cpp 92 | src/imagebuilder.cpp 93 | src/ini.cpp 94 | src/optimiser.cpp 95 | src/points.cpp 96 | src/processed.cpp 97 | src/settings.cpp 98 | src/songfile.cpp 99 | src/sp.cpp 100 | src/sptimemap.cpp 101 | src/stringutil.cpp 102 | resources/choptgui.exe.manifest 103 | resources/resources.qrc 104 | resources/resources.rc) 105 | target_include_directories( 106 | choptgui PRIVATE "${PROJECT_SOURCE_DIR}/include" "${PROJECT_SOURCE_DIR}/libs" ${PNG_INCLUDE_DIRS}) 107 | target_link_libraries(choptgui PRIVATE ${PNG_LIBRARIES} Qt6::Widgets sightread) 108 | 109 | set_property(TARGET choptgui PROPERTY POSITION_INDEPENDENT_CODE FALSE) 110 | 111 | if(APPLE) 112 | set_property(TARGET choptgui PROPERTY MACOSX_BUNDLE TRUE) 113 | endif() 114 | 115 | set_cpp_standard(choptgui) 116 | endif() 117 | 118 | include(CTest) 119 | 120 | option(PACKAGE_TESTS "Build the tests" ON) 121 | 122 | if(PACKAGE_TESTS) 123 | find_package(Boost 1.77 REQUIRED unit_test_framework) 124 | 125 | enable_testing() 126 | add_executable( 127 | chopt_tests 128 | tests/test_main.cpp 129 | tests/imagebuilder_unittest.cpp 130 | tests/ini_unittest.cpp 131 | tests/optimiser_unittest.cpp 132 | tests/points_unittest.cpp 133 | tests/processed_unittest.cpp 134 | tests/sp_unittest.cpp 135 | tests/stringutil_unittest.cpp 136 | src/imagebuilder.cpp 137 | src/ini.cpp 138 | src/optimiser.cpp 139 | src/points.cpp 140 | src/processed.cpp 141 | src/settings.cpp 142 | src/sp.cpp 143 | src/sptimemap.cpp 144 | src/stringutil.cpp) 145 | 146 | target_include_directories(chopt_tests 147 | PRIVATE "${PROJECT_SOURCE_DIR}/include") 148 | target_link_libraries(chopt_tests PRIVATE Boost::unit_test_framework Qt6::Core sightread) 149 | add_test(NAME chopt_tests COMMAND chopt_tests) 150 | set_cpp_standard(chopt_tests) 151 | set_warnings(chopt_tests) 152 | enable_sanitisers(chopt_tests) 153 | endif() 154 | 155 | option(BUILD_FUZZ_TARGETS "Build the fuzzing targets" OFF) 156 | 157 | if(BUILD_FUZZ_TARGETS) 158 | add_subdirectory(fuzzing_targets) 159 | endif() 160 | 161 | option(ENABLE_LTO "Enable Link Time Optimisation" OFF) 162 | 163 | if(ENABLE_LTO) 164 | include(CheckIPOSupported) 165 | check_ipo_supported( 166 | RESULT supported 167 | OUTPUT output 168 | LANGUAGES CXX) 169 | 170 | if(supported) 171 | set_property(TARGET chopt PROPERTY INTERPROCEDURAL_OPTIMIZATION TRUE) 172 | 173 | if(BUILD_GUI) 174 | set_property(TARGET choptgui PROPERTY INTERPROCEDURAL_OPTIMIZATION TRUE) 175 | endif() 176 | else() 177 | message(WARNING "LTO is not supported: ${output}") 178 | endif() 179 | endif() 180 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CHOpt 2 | 3 | A program to generate optimal Star Power paths for Clone Hero. 4 | 5 | [![Codacy Badge](https://app.codacy.com/project/badge/Grade/3e82e23473fc48779a486d9099d52e21)](https://app.codacy.com/gh/GenericMadScientist/CHOpt/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade) 6 | 7 | ## Install 8 | 9 | Download the latest version from the [Releases page](../../releases). If you're 10 | on Windows, you will need to have installed the latest 11 | [Visual Studio Redistributable](https://aka.ms/vs/17/release/vc_redist.x64.exe). 12 | 13 | ## Usage 14 | 15 | If you are unfamiliar with the intricacies of reading paths, read 16 | [this guide](misc/How-to-read-paths.md). 17 | 18 | CHOpt has two versions, a command-line version and a graphical version. The 19 | graphical version has the same options as the command-line version and is 20 | self-explanatory enough. As for the command-line version, an example usage to 21 | path Trogdor on Hard and save the output to trogdor_path.png is 22 | 23 | ```bat 24 | > CHOpt.exe -f trogdor.chart -d hard -o trogdor_path.png 25 | ``` 26 | 27 | Only the -f parameter is required, the difficulty defaults to Expert and the 28 | path is by default saved to path.png. The full list of arguments can be found 29 | by passing -h or --help to CHOpt, or by consulting the table below. 30 | 31 | | Arguments | Action | 32 | | ----------------------- | ----------------------------------------------------------------------------- | 33 | | -h, --help | List optional arguments | 34 | | -f, --file | Chart filename | 35 | | -o, --output | Filename of output image (.bmp or .png) | 36 | | -d, --diff | Difficulty (easy/medium/hard/expert) | 37 | | -i, --instrument | Instrument (guitar/coop/bass/rhythm/keys/ghl/ghlbass/drums/proguitar/probass) | 38 | | --sqz, --squeeze | Set squeeze % | 39 | | --ew, --early-whammy | Set early whammy % | 40 | | --lazy, --lazy-whammy | Set number of ms of whammy lost per sustain | 41 | | --delay, --whammy-delay | Amount of ms after each activation before whammy can be obtained | 42 | | --lag, --video-lag | Video calibration, in ms | 43 | | -s, --speed | Set speed the song is played at | 44 | | -l, --lefty-flip | Draw with lefty flip | 45 | | --no-double-kick | Disable 2x kick (drums only) | 46 | | --no-kick | Disable non-2x kicks (drums only) | 47 | | --no-pro-drums | Disable pro drums (drums only) | 48 | | --enable-dynamics | Enables double points from ghost and accent notes (drums only) | 49 | | --engine | Choose the engine (ch/fnf/gh1/rb/rb3) | 50 | | -p, --precision-mode | Enable precision mode (CH only) | 51 | | -b, --blank | Output a blank image without pathing | 52 | | --no-image | Do not create an image | 53 | | --no-bpms | Do not draw BPMs | 54 | | --no-solos | Do not draw solo sections | 55 | | --no-time-sigs | Do not draw time signatures | 56 | | --act-opacity | Set opacity of activations in images | 57 | 58 | If you would like to conveniently run CHOpt on a setlist and you happen to be 59 | on Windows, I made a PowerShell script that I've put [here](misc/setlist.ps1). 60 | Change the four variables then run the script. The simplest way to do that is 61 | probably to open the folder the script is in, double click the top bar and type 62 | in cmd then enter to open a command prompt in that folder, then run the command 63 | 64 | ```bat 65 | > powershell -ExecutionPolicy Bypass -File setlist.ps1 66 | ``` 67 | 68 | ## Dependencies 69 | 70 | * [Boost](https://www.boost.org) for Boost.Test 71 | * [CImg](https://cimg.eu) to produce images 72 | * [libpng](http://libpng.org/pub/png/libpng.html) to save pngs 73 | * [Qt 6](https://www.qt.io) for the GUI and various utility code for both 74 | versions 75 | * [SightRead](https://github.com/GenericMadScientist/SightRead) for parsing 76 | charts and midis 77 | * [zlib](https://zlib.net) is a dependency of libpng 78 | 79 | CImg is vendored in the repo. Boost, libpng, Qt, and zlib will need to be 80 | provided by anyone compiling CHOpt for themselves. Boost is only required for 81 | tests. libpng and zlib need to be set up so that 82 | [FindPNG](https://cmake.org/cmake/help/latest/module/FindPNG.html) can find 83 | them, and the same is true for Boost and Qt (see 84 | [this](https://cmake.org/cmake/help/latest/module/FindBoost.html) and 85 | [this](https://cmake.org/cmake/help/latest/manual/cmake-qt.7.html) page for 86 | details). SightRead is included a git submodule. 87 | 88 | ## Acknowledgements 89 | 90 | * FireFox2000000's Moonscraper .chart and .mid parsers were very helpful for 91 | getting an initial idea of Clone Hero's parsing behaviour. 92 | * Dinoguy1000 and shadoweh helped me make sure CHOpt runs on other peoples' 93 | machines. 94 | * Various users for feedback and testing, including 3-UP, ArchWK, Bromik, 95 | CyclopsDragon, DNelson, GHNerd, GiometriQ, Haggis, Jdsmitty1, Joel, Jpetersen5, 96 | Jrh, JUANPGP, Kyleruth, LightlessWalk, Littlejth, Lucretio, NicoBrenChan, 97 | RandomDays, RileyTheFox, Taka, Tposejank, Venxm, and Zantor. 98 | 99 | ## Contact 100 | 101 | If you have any bug reports I would prefer they be reported on the GitHub page, 102 | but feel free to send them to me on Discord at genericmadscientist. 103 | -------------------------------------------------------------------------------- /cmake/Sanitisers.cmake: -------------------------------------------------------------------------------- 1 | # Enable the selected sanitisers; only supports GCC and Clang 2 | function(enable_sanitisers target) 3 | if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES 4 | ".*Clang") 5 | set(sanitisers "") 6 | 7 | option(ENABLE_SANITISER_ADDRESS "Enable address sanitiser" OFF) 8 | if(ENABLE_SANITISER_ADDRESS) 9 | list(APPEND sanitisers "address") 10 | endif() 11 | 12 | option(ENABLE_SANITISER_LEAK "Enable leak sanitiser" OFF) 13 | if(ENABLE_SANITISER_LEAK) 14 | list(APPEND sanitisers "leak") 15 | endif() 16 | 17 | option(ENABLE_SANITISER_UNDEFINED_BEHAVIOUR 18 | "Enable undefined behaviour sanitiser" OFF) 19 | if(ENABLE_SANITISER_UNDEFINED_BEHAVIOUR) 20 | list(APPEND sanitisers "undefined") 21 | endif() 22 | 23 | option(ENABLE_SANITISER_THREAD "Enable thread sanitiser" OFF) 24 | if(ENABLE_SANITISER_THREAD) 25 | if("address" IN_LIST sanitisers OR "leak" IN_LIST sanitisers) 26 | message(WARNING "TSan does not work with ASan or LSan enabled") 27 | else() 28 | list(APPEND sanitisers "thread") 29 | endif() 30 | endif() 31 | 32 | option(ENABLE_SANITISER_MEMORY "Enable memory sanitiser" OFF) 33 | if(ENABLE_SANITISER_MEMORY AND CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") 34 | if("address" IN_LIST sanitisers 35 | OR "thread" IN_LIST sanitisers 36 | OR "leak" IN_LIST sanitisers) 37 | message(WARNING "MSan does not work with ASan, LSan, or TSan enabled") 38 | else() 39 | list(APPEND sanitisers "memory") 40 | endif() 41 | endif() 42 | 43 | list(JOIN sanitisers "," list_of_sanitisers) 44 | endif() 45 | 46 | if(list_of_sanitisers) 47 | if(NOT "${list_of_sanitisers}" STREQUAL "") 48 | target_compile_options(${target} PRIVATE -fsanitize=${list_of_sanitisers}) 49 | target_link_libraries(${target} PRIVATE -fsanitize=${list_of_sanitisers}) 50 | endif() 51 | endif() 52 | endfunction() 53 | -------------------------------------------------------------------------------- /fuzzing_targets/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(INCLUDE "${PROJECT_SOURCE_DIR}/include") 2 | set(LIBS "${PROJECT_SOURCE_DIR}/libs") 3 | set(SRC "${PROJECT_SOURCE_DIR}/src") 4 | 5 | # Adds a new fuzz target 6 | function(add_fuzz_target testname) 7 | add_executable(${testname} ${ARGN}) 8 | target_include_directories(${testname} PRIVATE ${INCLUDE} ${LIBS}) 9 | target_compile_options(${testname} PRIVATE -fsanitize=fuzzer) 10 | target_link_libraries(${testname} PRIVATE -fsanitize=fuzzer) 11 | set_cpp_standard(${testname}) 12 | set_warnings(${testname}) 13 | enable_sanitisers(${testname}) 14 | endfunction() 15 | 16 | add_fuzz_target(chart_fuzzer chart_fuzzer.cpp "${SRC}/chart.cpp" 17 | "${SRC}/stringutil.cpp") 18 | 19 | add_fuzz_target(midi_fuzzer midi_fuzzer.cpp "${SRC}/midi.cpp") 20 | 21 | add_fuzz_target( 22 | chart_song_fuzzer 23 | chart_song_fuzzer.cpp 24 | "${SRC}/chart.cpp" 25 | "${SRC}/ini.cpp" 26 | "${SRC}/midi.cpp" 27 | "${SRC}/song.cpp" 28 | "${SRC}/songparts.cpp" 29 | "${SRC}/stringutil.cpp") 30 | 31 | add_fuzz_target( 32 | midi_song_fuzzer 33 | midi_song_fuzzer.cpp 34 | "${SRC}/chart.cpp" 35 | "${SRC}/ini.cpp" 36 | "${SRC}/midi.cpp" 37 | "${SRC}/song.cpp" 38 | "${SRC}/songparts.cpp" 39 | "${SRC}/stringutil.cpp") 40 | -------------------------------------------------------------------------------- /fuzzing_targets/chart_fuzzer.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * CHOpt - Star Power optimiser for Clone Hero 3 | * Copyright (C) 2021 Raymond Wright 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include 20 | 21 | #include 22 | #include 23 | 24 | extern "C" int LLVMFuzzerTestOneInput(const char* data, size_t size) 25 | { 26 | const std::string_view input {data, size}; 27 | try { 28 | parse_chart(input); 29 | return 0; 30 | } catch (const ParseError&) { 31 | return 0; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /fuzzing_targets/chart_song_fuzzer.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * CHOpt - Star Power optimiser for Clone Hero 3 | * Copyright (C) 2021 Raymond Wright 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include 20 | 21 | #include 22 | 23 | #include "song.hpp" 24 | 25 | extern "C" int LLVMFuzzerTestOneInput(const char* data, size_t size) 26 | { 27 | const std::string_view input {data, size}; 28 | try { 29 | Song::from_chart(parse_chart(input), {}); 30 | return 0; 31 | } catch (const ParseError&) { 32 | return 0; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /fuzzing_targets/midi_fuzzer.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * CHOpt - Star Power optimiser for Clone Hero 3 | * Copyright (C) 2021 Raymond Wright 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include 20 | 21 | #include 22 | 23 | #include "midi.hpp" 24 | 25 | extern "C" int LLVMFuzzerTestOneInput(const char* data, size_t size) 26 | { 27 | const std::vector input {data, data + size}; 28 | try { 29 | SightRead::Detail::parse_midi(input); 30 | return 0; 31 | } catch (const ParseError&) { 32 | return 0; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /fuzzing_targets/midi_song_fuzzer.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * CHOpt - Star Power optimiser for Clone Hero 3 | * Copyright (C) 2021 Raymond Wright 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include 20 | 21 | #include 22 | 23 | #include "song.hpp" 24 | 25 | extern "C" int LLVMFuzzerTestOneInput(const char* data, size_t size) 26 | { 27 | const std::vector input {data, data + size}; 28 | try { 29 | Song::from_midi(parse_midi(input), {}); 30 | return 0; 31 | } catch (const ParseError&) { 32 | return 0; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /gui/json_settings.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * CHOpt - Star Power optimiser for Clone Hero 3 | * Copyright (C) 2022, 2024 Raymond Wright 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include "json_settings.hpp" 27 | 28 | namespace { 29 | struct IntRange { 30 | int min; 31 | int max; 32 | }; 33 | 34 | int read_value(const QJsonObject& settings, const QString& name, IntRange range, 35 | int default_value) 36 | { 37 | const auto value = settings.value(name).toInt(default_value); 38 | if (value >= range.min && value <= range.max) { 39 | return value; 40 | } 41 | return default_value; 42 | } 43 | 44 | bool read_json_bool(const QJsonObject& settings, const QString& name, 45 | bool default_value) 46 | { 47 | return settings.value(name).toBool(default_value); 48 | } 49 | 50 | QString settings_path(std::string_view application_dir) 51 | { 52 | const auto path = std::filesystem::path(application_dir) / "settings.json"; 53 | return QString::fromStdString(path.string()); 54 | } 55 | } 56 | 57 | JsonSettings load_saved_settings(std::string_view application_dir) 58 | { 59 | constexpr int MAX_LINE_EDIT_INT = 999999999; 60 | constexpr int MAX_PERCENT = 100; 61 | constexpr int MAX_VIDEO_LAG = 200; 62 | constexpr int MIN_VIDEO_LAG = -200; 63 | 64 | JsonSettings settings {}; 65 | settings.squeeze = MAX_PERCENT; 66 | settings.early_whammy = MAX_PERCENT; 67 | settings.lazy_whammy = 0; 68 | settings.whammy_delay = 0; 69 | settings.video_lag = 0; 70 | settings.is_lefty_flip = false; 71 | 72 | QFile settings_file {settings_path(application_dir)}; 73 | if (!settings_file.open(QIODevice::ReadOnly | QIODevice::Text)) { 74 | return settings; 75 | } 76 | 77 | const auto settings_file_bytes = settings_file.readAll(); 78 | const auto jv = QJsonDocument::fromJson(settings_file_bytes); 79 | if (jv.isNull() || !jv.isObject()) { 80 | return settings; 81 | } 82 | 83 | const auto obj = jv.object(); 84 | settings.squeeze 85 | = read_value(obj, "squeeze", {0, MAX_PERCENT}, MAX_PERCENT); 86 | settings.early_whammy 87 | = read_value(obj, "early_whammy", {0, MAX_PERCENT}, MAX_PERCENT); 88 | settings.lazy_whammy 89 | = read_value(obj, "lazy_whammy", {0, MAX_LINE_EDIT_INT}, 0); 90 | settings.whammy_delay 91 | = read_value(obj, "whammy_delay", {0, MAX_LINE_EDIT_INT}, 0); 92 | settings.video_lag 93 | = read_value(obj, "video_lag", {MIN_VIDEO_LAG, MAX_VIDEO_LAG}, 0); 94 | settings.is_lefty_flip = read_json_bool(obj, "lefty_flip", false); 95 | 96 | return settings; 97 | } 98 | 99 | void save_settings(const JsonSettings& settings, 100 | std::string_view application_dir) 101 | { 102 | const QJsonObject obj = {{"squeeze", settings.squeeze}, 103 | {"early_whammy", settings.early_whammy}, 104 | {"lazy_whammy", settings.lazy_whammy}, 105 | {"whammy_delay", settings.whammy_delay}, 106 | {"video_lag", settings.video_lag}, 107 | {"lefty_flip", settings.is_lefty_flip}}; 108 | QFile settings_file {settings_path(application_dir)}; 109 | if (settings_file.open(QIODevice::WriteOnly)) { 110 | settings_file.write(QJsonDocument(obj).toJson()); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /gui/json_settings.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * CHOpt - Star Power optimiser for Clone Hero 3 | * Copyright (C) 2022 Raymond Wright 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #ifndef CHOPT_JSON_SETTINGS_HPP 20 | #define CHOPT_JSON_SETTINGS_HPP 21 | 22 | #include 23 | 24 | struct JsonSettings { 25 | int squeeze; 26 | int early_whammy; 27 | int lazy_whammy; 28 | int whammy_delay; 29 | int video_lag; 30 | bool is_lefty_flip; 31 | }; 32 | 33 | JsonSettings load_saved_settings(std::string_view application_dir); 34 | void save_settings(const JsonSettings& settings, 35 | std::string_view application_dir); 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /gui/main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * CHOpt - Star Power optimiser for Clone Hero 3 | * Copyright (C) 2020, 2024 Raymond Wright 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include "mainwindow.hpp" 20 | 21 | #include 22 | #include 23 | 24 | int main(int argc, char* argv[]) 25 | { 26 | QApplication a(argc, argv); 27 | MainWindow w; 28 | w.setWindowIcon(QIcon(":/icon.png")); 29 | w.show(); 30 | return QApplication::exec(); 31 | } 32 | -------------------------------------------------------------------------------- /gui/mainwindow.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * CHOpt - Star Power optimiser for Clone Hero 3 | * Copyright (C) 2020, 2021, 2023, 2024 Raymond Wright 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #ifndef CHOPT_MAINWINDOW_HPP 20 | #define CHOPT_MAINWINDOW_HPP 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | #include 27 | #include 28 | #include 29 | 30 | #include 31 | 32 | #include "settings.hpp" 33 | #include "songfile.hpp" 34 | 35 | namespace Ui { 36 | class MainWindow; 37 | } 38 | 39 | class MainWindow : public QMainWindow { 40 | Q_OBJECT 41 | 42 | private: 43 | std::unique_ptr m_ui; 44 | std::optional m_loaded_file; 45 | std::unique_ptr m_thread; 46 | Settings get_settings() const; 47 | void load_file(const QString& file_name); 48 | void populate_games(const std::set& games); 49 | static constexpr int MAX_SPEED = 5000; 50 | static constexpr int MIN_SPEED = 5; 51 | 52 | protected: 53 | void dragEnterEvent(QDragEnterEvent* event) override; 54 | void dropEvent(QDropEvent* event) override; 55 | 56 | public: 57 | explicit MainWindow(QWidget* parent = nullptr); 58 | ~MainWindow(); 59 | 60 | private slots: 61 | void clear_worker_thread(); 62 | void on_earlyWhammySlider_valueChanged(int value); 63 | void on_engineComboBox_currentIndexChanged(int index); 64 | void on_findPathButton_clicked(); 65 | void on_instrumentComboBox_currentIndexChanged(int index); 66 | void on_opacitySlider_valueChanged(int value); 67 | void on_selectFileButton_clicked(); 68 | void on_squeezeSlider_valueChanged(int value); 69 | void on_videoLagSlider_valueChanged(int value); 70 | void parsing_failed(const QString& file_name); 71 | void path_found(); 72 | void song_read(SongFile loaded_file, const std::set& games, 73 | const QString& file_name); 74 | void write_message(const QString& message); 75 | }; 76 | 77 | #endif 78 | -------------------------------------------------------------------------------- /include/cimg_wrapper.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * CHOpt - Star Power optimiser for Clone Hero 3 | * Copyright (C) 2020, 2022 Raymond Wright 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | // The entire purpose of this header is to include CImg with warnings 20 | // disabled. On GCC and Clang this can be dealt with by using -Isystem, and the 21 | // MSVC equivalent seems to be to use /external:I (see 22 | // https://devblogs.microsoft.com/cppblog/broken-warnings-theory/). However, 23 | // clang-cl seems to not have this available yet so this hack will have to do. 24 | 25 | #ifndef CHOPT_CIMG_WRAPPER_HPP 26 | #define CHOPT_CIMG_WRAPPER_HPP 27 | 28 | #if defined(__clang__) 29 | #pragma clang diagnostic push 30 | #pragma clang diagnostic ignored "-Weverything" 31 | #elif defined(__GNUG__) 32 | #pragma GCC system_header 33 | #elif defined(_MSC_VER) 34 | #pragma warning(push, 0) 35 | #endif 36 | 37 | #define cimg_display 0 // NOLINT 38 | #define cimg_use_png 39 | #include "CImg.h" 40 | 41 | #if defined(__clang__) 42 | #pragma clang diagnostic pop 43 | #elif defined(_MSC_VER) 44 | #pragma warning(pop) 45 | #endif 46 | 47 | #endif -------------------------------------------------------------------------------- /include/image.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * CHOpt - Star Power optimiser for Clone Hero 3 | * Copyright (C) 2020, 2022 Raymond Wright 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #ifndef CHOPT_IMAGE_HPP 20 | #define CHOPT_IMAGE_HPP 21 | 22 | #include 23 | 24 | #include "imagebuilder.hpp" 25 | 26 | class ImageImpl; 27 | 28 | class Image { 29 | private: 30 | std::unique_ptr m_impl; 31 | 32 | public: 33 | explicit Image(const ImageBuilder& builder); 34 | ~Image(); 35 | Image(const Image&) = delete; 36 | Image(Image&& image) noexcept; 37 | Image& operator=(const Image&) = delete; 38 | Image& operator=(Image&& image) noexcept; 39 | 40 | void save(const char* filename) const; 41 | }; 42 | 43 | #endif 44 | -------------------------------------------------------------------------------- /include/imagebuilder.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * CHOpt - Star Power optimiser for Clone Hero 3 | * Copyright (C) 2020, 2021, 2022, 2023, 2025 Raymond Wright 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #ifndef CHOPT_IMAGEBUILDER_HPP 20 | #define CHOPT_IMAGEBUILDER_HPP 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #include "engine.hpp" 34 | #include "points.hpp" 35 | #include "processed.hpp" 36 | #include "sp.hpp" 37 | #include "sptimemap.hpp" 38 | 39 | struct DrawnRow { 40 | double start; 41 | double end; 42 | }; 43 | 44 | struct DrawnNote { 45 | double beat; 46 | std::array lengths; 47 | SightRead::NoteFlags note_flags; 48 | bool is_sp_note; 49 | }; 50 | 51 | class ImageBuilder { 52 | private: 53 | SightRead::TrackType m_track_type; 54 | SightRead::Difficulty m_difficulty; 55 | bool m_is_lefty_flip; 56 | std::vector m_rows; 57 | std::vector m_half_beat_lines; 58 | std::vector m_beat_lines; 59 | std::vector m_measure_lines; 60 | std::vector> m_bpms; 61 | std::vector> m_time_sigs; 62 | std::vector m_notes; 63 | std::vector m_base_values; 64 | std::vector m_score_values; 65 | std::vector m_sp_percent_values; 66 | std::vector m_sp_values; 67 | std::string m_song_name; 68 | std::string m_artist; 69 | std::string m_charter; 70 | std::vector> m_green_ranges; 71 | std::vector> m_blue_ranges; 72 | std::vector> m_red_ranges; 73 | std::vector> m_yellow_ranges; 74 | std::vector> m_solo_ranges; 75 | std::vector> m_practice_sections; 76 | std::vector> m_bre_ranges; 77 | std::vector> m_fill_ranges; 78 | std::vector> m_unison_ranges; 79 | float m_activation_opacity {0.33F}; 80 | int m_total_score {0}; 81 | bool m_overlap_engine {true}; 82 | 83 | void form_beat_lines(const SightRead::TempoMap& tempo_map); 84 | static bool is_neutralised_phrase(SightRead::Beat note_pos, 85 | const Path& path); 86 | std::tuple 87 | sp_phrase_bounds(const SightRead::StarPower& phrase, 88 | const SightRead::NoteTrack& track, const Path& path) const; 89 | 90 | public: 91 | ImageBuilder(const SightRead::NoteTrack& track, 92 | SightRead::Difficulty difficulty, 93 | const SightRead::DrumSettings& drum_settings, 94 | bool is_lefty_flip, bool is_overlap_engine); 95 | void add_bpms(const SightRead::TempoMap& tempo_map); 96 | void add_bre(const SightRead::BigRockEnding& bre, 97 | const SightRead::TempoMap& tempo_map); 98 | void add_drum_fills(const SightRead::NoteTrack& track); 99 | void add_measure_values(const PointSet& points, 100 | const SightRead::TempoMap& tempo_map, 101 | const Path& path); 102 | void add_practice_sections( 103 | const std::vector& practice_sections, 104 | const SightRead::TempoMap& tempo_map); 105 | void add_solo_sections(const std::vector& solos, 106 | const SightRead::TempoMap& tempo_map); 107 | void add_song_header(const SightRead::SongGlobalData& global_data); 108 | void add_sp_acts(const PointSet& points, 109 | const SightRead::TempoMap& tempo_map, const Path& path); 110 | void add_sp_percent_values(const SpData& sp_data, const SpTimeMap& time_map, 111 | const PointSet& points, const Path& path, 112 | const SpEngineValues& sp_engine_values); 113 | void add_sp_phrases(const SightRead::NoteTrack& track, 114 | const std::vector& unison_phrases, 115 | const Path& path); 116 | void add_sp_values(const SpData& sp_data, const Engine& engine); 117 | void add_time_sigs(const SightRead::TempoMap& tempo_map); 118 | void set_total_score(const PointSet& points, 119 | const std::vector& solos, 120 | const Path& path); 121 | 122 | [[nodiscard]] const std::string& artist() const { return m_artist; } 123 | [[nodiscard]] const std::vector& base_values() const 124 | { 125 | return m_base_values; 126 | } 127 | [[nodiscard]] const std::vector& beat_lines() const 128 | { 129 | return m_beat_lines; 130 | } 131 | [[nodiscard]] const std::vector>& 132 | blue_ranges() const 133 | { 134 | return m_blue_ranges; 135 | } 136 | [[nodiscard]] const std::vector>& bpms() const 137 | { 138 | return m_bpms; 139 | } 140 | [[nodiscard]] const std::vector>& 141 | bre_ranges() const 142 | { 143 | return m_bre_ranges; 144 | } 145 | [[nodiscard]] const std::string& charter() const { return m_charter; } 146 | [[nodiscard]] const std::vector>& 147 | fill_ranges() const 148 | { 149 | return m_fill_ranges; 150 | } 151 | [[nodiscard]] const std::vector>& 152 | green_ranges() const 153 | { 154 | return m_green_ranges; 155 | } 156 | [[nodiscard]] const std::vector& half_beat_lines() const 157 | { 158 | return m_half_beat_lines; 159 | } 160 | [[nodiscard]] const std::vector& measure_lines() const 161 | { 162 | return m_measure_lines; 163 | } 164 | [[nodiscard]] const std::vector& notes() const 165 | { 166 | return m_notes; 167 | } 168 | [[nodiscard]] const std::vector>& 169 | practice_sections() const 170 | { 171 | return m_practice_sections; 172 | } 173 | [[nodiscard]] const std::vector>& 174 | red_ranges() const 175 | { 176 | return m_red_ranges; 177 | } 178 | [[nodiscard]] const std::vector& rows() const { return m_rows; } 179 | [[nodiscard]] const std::vector& score_values() const 180 | { 181 | return m_score_values; 182 | } 183 | [[nodiscard]] const std::vector>& 184 | solo_ranges() const 185 | { 186 | return m_solo_ranges; 187 | } 188 | [[nodiscard]] const std::string& song_name() const { return m_song_name; } 189 | [[nodiscard]] const std::vector& sp_percent_values() const 190 | { 191 | return m_sp_percent_values; 192 | } 193 | [[nodiscard]] const std::vector& sp_values() const 194 | { 195 | return m_sp_values; 196 | } 197 | [[nodiscard]] const std::vector>& 198 | time_sigs() const 199 | { 200 | return m_time_sigs; 201 | } 202 | [[nodiscard]] SightRead::TrackType track_type() const 203 | { 204 | return m_track_type; 205 | } 206 | [[nodiscard]] const std::vector>& 207 | unison_ranges() const 208 | { 209 | return m_unison_ranges; 210 | } 211 | [[nodiscard]] const std::vector>& 212 | yellow_ranges() const 213 | { 214 | return m_yellow_ranges; 215 | } 216 | [[nodiscard]] float activation_opacity() const 217 | { 218 | return m_activation_opacity; 219 | } 220 | float& activation_opacity() { return m_activation_opacity; } 221 | [[nodiscard]] int total_score() const { return m_total_score; } 222 | [[nodiscard]] SightRead::Difficulty difficulty() const 223 | { 224 | return m_difficulty; 225 | } 226 | [[nodiscard]] bool is_lefty_flip() const { return m_is_lefty_flip; } 227 | }; 228 | 229 | ImageBuilder make_builder(SightRead::Song& song, 230 | const SightRead::NoteTrack& track, 231 | const Settings& settings, 232 | const std::function& write, 233 | const std::atomic* terminate); 234 | 235 | #endif 236 | -------------------------------------------------------------------------------- /include/ini.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * CHOpt - Star Power optimiser for Clone Hero 3 | * Copyright (C) 2020 Raymond Wright 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #ifndef CHOPT_INI_HPP 20 | #define CHOPT_INI_HPP 21 | 22 | #include 23 | 24 | #include 25 | 26 | SightRead::Metadata parse_ini(std::string_view data); 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /include/optimiser.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * CHOpt - Star Power optimiser for Clone Hero 3 | * Copyright (C) 2020, 2021, 2023 Raymond Wright 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #ifndef CHOPT_OPTIMISER_HPP 20 | #define CHOPT_OPTIMISER_HPP 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include 31 | 32 | #include "points.hpp" 33 | #include "processed.hpp" 34 | 35 | // The class that stores extra information needed on top of a ProcessedSong for 36 | // the purposes of optimisation, and finds the optimal path. The song passed to 37 | // Optimiser's constructor must outlive Optimiser; the class is done this way so 38 | // that other code can make use of the PointIters that are returned by Optimiser 39 | // without needing access to Optimiser itself. 40 | class Optimiser { 41 | private: 42 | // The Cache is used to store paths starting from a certain point onwards, 43 | // i.e., the solution to our subproblems in our dynamic programming 44 | // algorithm. Cache.full_sp_paths represents the best path with the first 45 | // activation at the point key or later, under the condition there is 46 | // already full SP there. 47 | struct CacheKey { 48 | PointPtr point; 49 | SpPosition position {SightRead::Beat(0.0), SpMeasure(0.0)}; 50 | 51 | friend bool operator<(const CacheKey& lhs, const CacheKey& rhs) 52 | { 53 | return std::tie(lhs.point, lhs.position.beat) 54 | < std::tie(rhs.point, rhs.position.beat); 55 | } 56 | }; 57 | 58 | struct CacheValue { 59 | std::vector> possible_next_acts; 60 | int score_boost; 61 | }; 62 | 63 | struct Cache { 64 | std::map paths; 65 | std::map full_sp_paths; 66 | }; 67 | 68 | // The idea is this is like a std::set, but is add-only and takes 69 | // advantage of the fact that we often tend to add all elements before a 70 | // certain point. 71 | class PointPtrRangeSet { 72 | private: 73 | PointPtr m_start; 74 | PointPtr m_end; 75 | PointPtr m_min_absent_ptr; 76 | std::vector m_abnormal_elements; 77 | 78 | public: 79 | PointPtrRangeSet(PointPtr start, PointPtr end) 80 | : m_start {start} 81 | , m_end {end} 82 | , m_min_absent_ptr {start} 83 | { 84 | assert(start < end); // NOLINT 85 | } 86 | 87 | [[nodiscard]] bool contains(PointPtr element) const 88 | { 89 | if (m_start > element || m_end <= element) { 90 | return false; 91 | } 92 | if (element < m_min_absent_ptr) { 93 | return true; 94 | } 95 | return std::find(m_abnormal_elements.cbegin(), 96 | m_abnormal_elements.cend(), element) 97 | != m_abnormal_elements.cend(); 98 | } 99 | 100 | [[nodiscard]] PointPtr lowest_absent_element() const 101 | { 102 | return m_min_absent_ptr; 103 | } 104 | 105 | void add(PointPtr element) 106 | { 107 | assert(m_start <= element); // NOLINT 108 | assert(element < m_end); // NOLINT 109 | if (m_min_absent_ptr == element) { 110 | ++m_min_absent_ptr; 111 | while (true) { 112 | auto next_elem_iter = std::find(m_abnormal_elements.begin(), 113 | m_abnormal_elements.end(), 114 | m_min_absent_ptr); 115 | if (next_elem_iter == m_abnormal_elements.end()) { 116 | return; 117 | } 118 | std::swap(*next_elem_iter, m_abnormal_elements.back()); 119 | m_abnormal_elements.pop_back(); 120 | ++m_min_absent_ptr; 121 | } 122 | } else { 123 | m_abnormal_elements.push_back(element); 124 | } 125 | } 126 | }; 127 | 128 | static constexpr double NEG_INF = -std::numeric_limits::infinity(); 129 | static constexpr double BASE_DRUM_FILL_DELAY = 2.0 * 100; 130 | const ProcessedSong* m_song; 131 | const std::atomic* m_terminate; 132 | const SightRead::Second m_drum_fill_delay; 133 | SightRead::Second m_whammy_delay; 134 | std::vector m_next_candidate_points; 135 | 136 | [[nodiscard]] PointPtr next_candidate_point(PointPtr point) const; 137 | [[nodiscard]] CacheKey advance_cache_key(CacheKey key) const; 138 | [[nodiscard]] CacheKey add_whammy_delay(CacheKey key) const; 139 | [[nodiscard]] std::optional 140 | try_previous_best_subpaths(CacheKey key, const Cache& cache, 141 | bool has_full_sp) const; 142 | CacheValue find_best_subpaths(CacheKey key, Cache& cache, 143 | bool has_full_sp) const; 144 | int get_partial_path(CacheKey key, Cache& cache) const; 145 | int get_partial_full_sp_path(PointPtr point, Cache& cache) const; 146 | [[nodiscard]] double act_squeeze_level(ProtoActivation act, 147 | CacheKey key) const; 148 | [[nodiscard]] SpPosition forced_whammy_end(ProtoActivation act, 149 | CacheKey key, 150 | double sqz_level) const; 151 | [[nodiscard]] std::tuple 152 | act_duration(ProtoActivation act, CacheKey key, double sqz_level, 153 | SpPosition min_whammy_force) const; 154 | [[nodiscard]] SightRead::Second 155 | earliest_fill_appearance(CacheKey key, bool has_full_sp) const; 156 | void complete_subpath( 157 | PointPtr p, SpPosition starting_pos, SpBar sp_bar, 158 | PointPtrRangeSet& attained_act_ends, Cache& cache, 159 | int& best_score_boost, 160 | std::vector>& acts) const; 161 | 162 | public: 163 | Optimiser(const ProcessedSong* song, const std::atomic* terminate, 164 | int speed, SightRead::Second whammy_delay); 165 | // Return the optimal Star Power path. 166 | [[nodiscard]] Path optimal_path() const; 167 | }; 168 | 169 | #endif 170 | -------------------------------------------------------------------------------- /include/points.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * CHOpt - Star Power optimiser for Clone Hero 3 | * Copyright (C) 2020, 2021, 2022, 2023, 2025 Raymond Wright 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #ifndef CHOPT_POINTS_HPP 20 | #define CHOPT_POINTS_HPP 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include 29 | #include 30 | #include 31 | 32 | #include "engine.hpp" 33 | #include "settings.hpp" 34 | #include "sptimemap.hpp" 35 | 36 | // fill_start is used for Drums, giving the start of the fill that makes a point 37 | // an activation note if it is one, or nullopt otherwise. 38 | struct Point { 39 | SpPosition position; 40 | SpPosition hit_window_start; 41 | SpPosition hit_window_end; 42 | SpPosition max_sqz_hit_window_start; 43 | std::optional fill_start; 44 | int value; 45 | int base_value; 46 | bool is_hold_point; 47 | bool is_sp_granting_note; 48 | bool is_unison_sp_granting_note; 49 | }; 50 | 51 | using PointPtr = std::vector::const_iterator; 52 | 53 | class PointSet { 54 | private: 55 | std::vector m_points; 56 | std::vector m_first_after_current_sp; 57 | std::vector m_next_non_hold_point; 58 | std::vector m_next_sp_granting_note; 59 | std::vector> m_solo_boosts; 60 | std::vector m_cumulative_score_totals; 61 | SightRead::Second m_video_lag; 62 | std::vector m_colours; 63 | 64 | public: 65 | PointSet(const SightRead::NoteTrack& track, 66 | const SpDurationData& duration_data, 67 | const PathingSettings& pathing_settings); 68 | [[nodiscard]] PointPtr cbegin() const { return m_points.cbegin(); } 69 | [[nodiscard]] PointPtr cend() const { return m_points.cend(); } 70 | // Designed for engines without SP overlap, so the next activation is not 71 | // using part of the given phrase. If the point is not part of a phrase, or 72 | // the engine supports overlap, then this just returns the next point. 73 | [[nodiscard]] PointPtr first_after_current_phrase(PointPtr point) const; 74 | [[nodiscard]] PointPtr next_non_hold_point(PointPtr point) const; 75 | [[nodiscard]] PointPtr next_sp_granting_note(PointPtr point) const; 76 | [[nodiscard]] std::string colour_set(PointPtr point) const 77 | { 78 | return m_colours[static_cast( 79 | std::distance(m_points.cbegin(), point))]; 80 | } 81 | // Get the combined score of all points that are >= start and < end. 82 | [[nodiscard]] int range_score(PointPtr start, PointPtr end) const; 83 | [[nodiscard]] const std::vector>& 84 | solo_boosts() const 85 | { 86 | return m_solo_boosts; 87 | } 88 | [[nodiscard]] SightRead::Second video_lag() const { return m_video_lag; } 89 | }; 90 | 91 | #endif 92 | -------------------------------------------------------------------------------- /include/processed.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * CHOpt - Star Power optimiser for Clone Hero 3 | * Copyright (C) 2020, 2021, 2022, 2023, 2024, 2025 Raymond Wright 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #ifndef CHOPT_PROCESSED_HPP 20 | #define CHOPT_PROCESSED_HPP 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #include "engine.hpp" 34 | #include "points.hpp" 35 | #include "settings.hpp" 36 | #include "sp.hpp" 37 | #include "sptimemap.hpp" 38 | 39 | struct ActivationCandidate { 40 | PointPtr act_start; 41 | PointPtr act_end; 42 | SpPosition earliest_activation_point; 43 | SpBar sp_bar; 44 | }; 45 | 46 | struct ProtoActivation { 47 | PointPtr act_start; 48 | PointPtr act_end; 49 | }; 50 | 51 | struct Activation { 52 | PointPtr act_start; 53 | PointPtr act_end; 54 | SightRead::Beat whammy_end {0.0}; 55 | SightRead::Beat sp_start {0.0}; 56 | SightRead::Beat sp_end {0.0}; 57 | }; 58 | 59 | // Part of the return value of ProcessedSong::is_candidate_valid. Says if an 60 | // activation is valid, and if not whether the problem is too little or too much 61 | // Star Power. 62 | enum class ActValidity { success, insufficient_sp, surplus_sp }; 63 | 64 | // Return value of ProcessedSong::is_candidate_valid, providing information on 65 | // whether an activation is valid, and if so the earliest position it can end. 66 | struct ActResult { 67 | SpPosition ending_position; 68 | ActValidity validity; 69 | }; 70 | 71 | struct Path { 72 | std::vector activations; 73 | int score_boost {0}; 74 | }; 75 | 76 | // Represents a song processed for Star Power optimisation. The constructor 77 | // should only fail due to OOM; invariants on the song are supposed to be 78 | // upheld by the constructors of the arguments. 79 | class ProcessedSong { 80 | private: 81 | static constexpr double NEG_INF = -std::numeric_limits::infinity(); 82 | 83 | SpTimeMap m_time_map; 84 | PointSet m_points; 85 | SpData m_sp_data; 86 | SpEngineValues m_sp_engine_values; 87 | int m_total_bre_boost; 88 | int m_total_solo_boost; 89 | int m_base_score; 90 | bool m_ignore_average_multiplier; 91 | bool m_is_drums; 92 | bool m_overlaps; 93 | 94 | SpBar sp_from_phrases(PointPtr begin, PointPtr end) const; 95 | std::vector act_summaries(const Path& path) const; 96 | std::vector drum_act_summaries(const Path& path) const; 97 | void append_activation(std::stringstream& stream, 98 | const Activation& activation, 99 | const std::string& act_summary) const; 100 | 101 | // This static function is necessary to deal with a bug in MSVC. See 102 | // https://developercommunity.visualstudio.com/t/ICE-with-MSVC-1940-with-default-functio/10750601 103 | // for details. 104 | static SpPosition default_position() 105 | { 106 | return {SightRead::Beat {NEG_INF}, SpMeasure {NEG_INF}}; 107 | } 108 | 109 | public: 110 | ProcessedSong(const SightRead::NoteTrack& track, 111 | const SpDurationData& duration_data, 112 | const PathingSettings& pathing_settings); 113 | 114 | // Return the minimum and maximum amount of SP can be acquired between two 115 | // points. Does not include SP from the point act_start. first_point is 116 | // given for the purposes of counting SP grantings notes, e.g. if start is 117 | // after the middle of first_point's timing window. All whammy up to 118 | // required_whammy_end is mandatory. 119 | [[nodiscard]] SpBar total_available_sp(SightRead::Beat start, 120 | PointPtr first_point, 121 | PointPtr act_start, 122 | SightRead::Beat required_whammy_end 123 | = SightRead::Beat {NEG_INF}) const; 124 | // Similar to total_available_sp, but no whammy is required and if it is 125 | // possible to get a half bar then the earliest position >= 126 | // earliest_potential_pos that grants a half bar is returned along with the 127 | // SP only up to that position. 128 | [[nodiscard]] std::tuple 129 | total_available_sp_with_earliest_pos( 130 | SightRead::Beat start, PointPtr first_point, PointPtr act_start, 131 | SpPosition earliest_potential_pos) const; 132 | // Returns an ActResult which says if an activation is valid, and if so the 133 | // earliest position it can end. Checks squeezes against the given amount 134 | // only. 135 | [[nodiscard]] ActResult is_candidate_valid( 136 | const ActivationCandidate& activation, double squeeze = 1.0, 137 | SpPosition required_whammy_end = default_position()) const; 138 | // Return the summary of a path. 139 | [[nodiscard]] std::string path_summary(const Path& path) const; 140 | 141 | // Return the position that is (100 - squeeze)% along the start of point's 142 | // timing window. 143 | [[nodiscard]] SpPosition adjusted_hit_window_start(PointPtr point, 144 | double squeeze) const; 145 | // Return the position that is squeeze% along the end of point's timing 146 | // window. 147 | [[nodiscard]] SpPosition adjusted_hit_window_end(PointPtr point, 148 | double squeeze) const; 149 | 150 | [[nodiscard]] const PointSet& points() const { return m_points; } 151 | [[nodiscard]] const SpData& sp_data() const { return m_sp_data; } 152 | [[nodiscard]] const SpTimeMap& sp_time_map() const { return m_time_map; } 153 | [[nodiscard]] bool is_drums() const { return m_is_drums; } 154 | [[nodiscard]] const SpEngineValues& sp_engine_values() const 155 | { 156 | return m_sp_engine_values; 157 | } 158 | }; 159 | 160 | #endif 161 | -------------------------------------------------------------------------------- /include/settings.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * CHOpt - Star Power optimiser for Clone Hero 3 | * Copyright (C) 2020, 2021, 2022, 2023, 2024, 2025 Raymond Wright 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #ifndef CHOPT_SETTINGS_HPP 20 | #define CHOPT_SETTINGS_HPP 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | #include 27 | 28 | #include 29 | #include 30 | #include 31 | 32 | #include "engine.hpp" 33 | 34 | enum class Game { 35 | CloneHero, 36 | FortniteFestival, 37 | GuitarHeroOne, 38 | RockBand, 39 | RockBandThree 40 | }; 41 | 42 | std::unique_ptr game_to_engine(Game game, 43 | SightRead::Instrument instrument, 44 | bool precision_mode); 45 | 46 | struct PathingSettings { 47 | std::unique_ptr engine; 48 | double squeeze; 49 | double early_whammy; 50 | SightRead::Second lazy_whammy {0.0}; 51 | SightRead::Second video_lag {0.0}; 52 | SightRead::Second whammy_delay {0.0}; 53 | SightRead::DrumSettings drum_settings; 54 | }; 55 | 56 | // This struct represents the options chosen on the command line by the user. 57 | struct Settings { 58 | bool blank; 59 | std::string filename; 60 | std::string image_path; 61 | bool draw_image; 62 | bool draw_bpms; 63 | bool draw_solos; 64 | bool draw_time_sigs; 65 | SightRead::Difficulty difficulty; 66 | SightRead::Instrument instrument; 67 | int speed; 68 | bool is_lefty_flip; 69 | Game game; 70 | PathingSettings pathing_settings; 71 | float opacity; 72 | }; 73 | 74 | // Parses the command line options. 75 | Settings from_args(const QStringList& args); 76 | 77 | #endif 78 | -------------------------------------------------------------------------------- /include/songfile.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * CHOpt - Star Power optimiser for Clone Hero 3 | * Copyright (C) 2023 Raymond Wright 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #ifndef CHOPT_SONGFILE_HPP 20 | #define CHOPT_SONGFILE_HPP 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | #include 27 | #include 28 | 29 | #include "settings.hpp" 30 | 31 | class SongFile { 32 | private: 33 | enum class FileType { Chart, Midi }; 34 | 35 | std::vector m_loaded_file; 36 | SightRead::Metadata m_metadata; 37 | FileType m_file_type; 38 | 39 | public: 40 | explicit SongFile(const std::string& filename); 41 | SightRead::Song load_song(Game game) const; 42 | }; 43 | 44 | #endif 45 | -------------------------------------------------------------------------------- /include/sp.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * CHOpt - Star Power optimiser for Clone Hero 3 | * Copyright (C) 2020, 2021, 2023, 2025 Raymond Wright 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #ifndef CHOPT_SP_HPP 20 | #define CHOPT_SP_HPP 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include 28 | #include 29 | #include 30 | 31 | #include "engine.hpp" 32 | #include "settings.hpp" 33 | #include "sptimemap.hpp" 34 | 35 | // Represents the minimum and maximum SP possible at a given time. 36 | class SpBar { 37 | private: 38 | double m_min; 39 | double m_max; 40 | SpEngineValues m_sp_engine_values; 41 | 42 | public: 43 | SpBar(double min, double max, const SpEngineValues& sp_engine_values) 44 | : m_min {min} 45 | , m_max {max} 46 | , m_sp_engine_values {sp_engine_values} 47 | { 48 | } 49 | 50 | double& min() { return m_min; } 51 | double& max() { return m_max; } 52 | [[nodiscard]] double min() const { return m_min; } 53 | [[nodiscard]] double max() const { return m_max; } 54 | 55 | void add_phrase() 56 | { 57 | m_min = std::min(m_min + m_sp_engine_values.phrase_amount, 1.0); 58 | m_max = std::min(m_max + m_sp_engine_values.phrase_amount, 1.0); 59 | } 60 | 61 | void add_unison_phrase() 62 | { 63 | m_min = std::min(m_min + m_sp_engine_values.unison_phrase_amount, 1.0); 64 | m_max = std::min(m_max + m_sp_engine_values.unison_phrase_amount, 1.0); 65 | } 66 | 67 | [[nodiscard]] bool full_enough_to_activate() const 68 | { 69 | return m_max >= m_sp_engine_values.minimum_to_activate; 70 | } 71 | }; 72 | 73 | // This is used by the optimiser to calculate SP drain. 74 | class SpData { 75 | private: 76 | struct BeatRate { 77 | SightRead::Beat position; 78 | double net_sp_gain_rate; 79 | }; 80 | 81 | struct WhammyRange { 82 | SpPosition start; 83 | SpPosition end; 84 | SightRead::Beat note; 85 | }; 86 | 87 | struct WhammyPropagationState { 88 | std::vector::const_iterator current_beat_rate; 89 | SightRead::Beat current_position; 90 | double current_sp; 91 | }; 92 | 93 | static constexpr double DEFAULT_BEATS_PER_BAR = 32.0; 94 | static constexpr double MEASURES_PER_BAR = 8.0; 95 | 96 | SpTimeMap m_time_map; 97 | std::vector m_beat_rates; 98 | std::vector m_whammy_ranges; 99 | SightRead::Beat m_last_whammy_point { 100 | -std::numeric_limits::infinity()}; 101 | std::vector::const_iterator> m_initial_guesses; 102 | const double m_sp_gain_rate; 103 | const double m_default_net_sp_gain_rate; 104 | 105 | static std::vector 106 | form_beat_rates(const SightRead::TempoMap& tempo_map, 107 | const std::vector& od_beats, 108 | const Engine& engine); 109 | 110 | [[nodiscard]] double 111 | propagate_over_whammy_range(SightRead::Beat start, SightRead::Beat end, 112 | double sp_bar_amount) const; 113 | [[nodiscard]] SightRead::Beat 114 | whammy_propagation_endpoint(SightRead::Beat start, SightRead::Beat end, 115 | double sp_bar_amount) const; 116 | [[nodiscard]] std::vector::const_iterator 117 | first_whammy_range_after(SightRead::Beat pos) const; 118 | [[nodiscard]] WhammyPropagationState 119 | initial_whammy_prop_state(SightRead::Beat start, SightRead::Beat end, 120 | double sp_bar_amount) const; 121 | SpPosition sp_drain_end_point(SpPosition start, double sp_bar_amount) const; 122 | 123 | public: 124 | SpData(const SightRead::NoteTrack& track, 125 | const SpDurationData& duration_data, 126 | const PathingSettings& pathing_settings); 127 | // Return the maximum amount of SP available at the end after propagating 128 | // over a range, or -1 if SP runs out at any point. Only includes SP gain 129 | // from whammy. 130 | [[nodiscard]] double propagate_sp_over_whammy_max(SpPosition start, 131 | SpPosition end, 132 | double sp) const; 133 | // Return the minimum amount of SP is available at the end after propagating 134 | // over a range, returning 0.0 if the minimum would hypothetically be 135 | // negative. 136 | [[nodiscard]] double 137 | propagate_sp_over_whammy_min(SpPosition start, SpPosition end, double sp, 138 | SpPosition required_whammy_end) const; 139 | // Return if a beat is at a place that can be whammied. 140 | [[nodiscard]] bool is_in_whammy_ranges(SightRead::Beat beat) const; 141 | // Return the amount of whammy obtainable across a range. 142 | [[nodiscard]] double available_whammy(SightRead::Beat start, 143 | SightRead::Beat end) const; 144 | // Return the amount of whammy obtainable across a range, from notes before 145 | // note_pos. 146 | [[nodiscard]] double available_whammy(SightRead::Beat start, 147 | SightRead::Beat end, 148 | SightRead::Beat note_pos) const; 149 | // Return how far an activation can propagate based on whammy, returning the 150 | // end of the range if it can be reached. 151 | [[nodiscard]] SpPosition activation_end_point(SpPosition start, 152 | SpPosition end, 153 | double sp_bar_amount) const; 154 | }; 155 | 156 | #endif 157 | -------------------------------------------------------------------------------- /include/sptimemap.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * CHOpt - Star Power optimiser for Clone Hero 3 | * Copyright (C) 2023, 2025 Raymond Wright 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #ifndef CHOPT_SPTIMEMAP_HPP 20 | #define CHOPT_SPTIMEMAP_HPP 21 | 22 | #include 23 | 24 | #include 25 | 26 | class SpMeasure { 27 | private: 28 | double m_value; 29 | 30 | public: 31 | explicit SpMeasure(double value) 32 | : m_value {value} 33 | { 34 | } 35 | [[nodiscard]] double value() const { return m_value; } 36 | [[nodiscard]] SightRead::Beat to_beat(double beat_rate) const 37 | { 38 | return SightRead::Beat(m_value * beat_rate); 39 | } 40 | 41 | std::partial_ordering operator<=>(const SpMeasure& rhs) const 42 | { 43 | return m_value <=> rhs.m_value; 44 | } 45 | 46 | SpMeasure& operator+=(const SpMeasure& rhs) 47 | { 48 | m_value += rhs.m_value; 49 | return *this; 50 | } 51 | 52 | SpMeasure& operator-=(const SpMeasure& rhs) 53 | { 54 | m_value -= rhs.m_value; 55 | return *this; 56 | } 57 | 58 | SpMeasure& operator*=(double rhs) 59 | { 60 | m_value *= rhs; 61 | return *this; 62 | } 63 | 64 | friend SpMeasure operator+(SpMeasure lhs, const SpMeasure& rhs) 65 | { 66 | lhs += rhs; 67 | return lhs; 68 | } 69 | 70 | friend SpMeasure operator-(SpMeasure lhs, const SpMeasure& rhs) 71 | { 72 | lhs -= rhs; 73 | return lhs; 74 | } 75 | 76 | friend SpMeasure operator*(SpMeasure lhs, double rhs) 77 | { 78 | lhs *= rhs; 79 | return lhs; 80 | } 81 | 82 | friend double operator/(const SpMeasure& lhs, const SpMeasure& rhs) 83 | { 84 | return lhs.m_value / rhs.m_value; 85 | } 86 | }; 87 | 88 | struct SpPosition { 89 | SightRead::Beat beat; 90 | SpMeasure sp_measure; 91 | }; 92 | 93 | enum class SpMode { Measure, OdBeat }; 94 | 95 | class SpTimeMap { 96 | private: 97 | SightRead::TempoMap m_tempo_map; 98 | SpMode m_sp_mode; 99 | 100 | public: 101 | SpTimeMap(SightRead::TempoMap tempo_map, SpMode sp_mode) 102 | : m_tempo_map {std::move(tempo_map)} 103 | , m_sp_mode {sp_mode} 104 | { 105 | } 106 | 107 | [[nodiscard]] SightRead::Beat to_beats(SightRead::Second seconds) const; 108 | [[nodiscard]] SightRead::Beat to_beats(SpMeasure sp_measures) const; 109 | [[nodiscard]] SightRead::Beat to_beats(SightRead::Tick ticks) const; 110 | 111 | [[nodiscard]] SightRead::Second to_seconds(SightRead::Beat beats) const; 112 | [[nodiscard]] SightRead::Second to_seconds(SpMeasure sp_measures) const; 113 | 114 | [[nodiscard]] SpMeasure to_sp_measures(SightRead::Beat beats) const; 115 | [[nodiscard]] SpMeasure to_sp_measures(SightRead::Second seconds) const; 116 | }; 117 | 118 | struct SpDurationData { 119 | SpTimeMap time_map; 120 | std::vector od_beats; 121 | std::vector unison_phrases; 122 | }; 123 | 124 | #endif 125 | -------------------------------------------------------------------------------- /include/stringutil.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * CHOpt - Star Power optimiser for Clone Hero 3 | * Copyright (C) 2020, 2021, 2023 Raymond Wright 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #ifndef CHOPT_STRINGUTIL_HPP 20 | #define CHOPT_STRINGUTIL_HPP 21 | 22 | #include 23 | #include 24 | 25 | // This returns a string_view from the start of input until a carriage return 26 | // or newline. input is changed to point to the first character past the 27 | // detected newline character that is not a whitespace character. 28 | std::string_view break_off_newline(std::string_view& input); 29 | 30 | std::string_view skip_whitespace(std::string_view input); 31 | 32 | std::string to_ordinal(int ordinal); 33 | 34 | // Convert a UTF-8 or UTF-16le string to a UTF-8 string. 35 | std::string to_utf8_string(std::string_view input); 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /integration_tests/run_integration_tests.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sqlite3 3 | import subprocess 4 | 5 | program = "build/chopt" 6 | song_dir = "integration_tests/songs" 7 | 8 | conn = sqlite3.connect("integration_tests/tests.db") 9 | c = conn.cursor() 10 | 11 | c.execute( 12 | ( 13 | "select song_id, file, difficulty, instrument, path, base_score, total_score, " 14 | "avg_mult, engine, squeeze, early_whammy, speed from Songs" 15 | ) 16 | ) 17 | songs = list(c.fetchall()) 18 | 19 | outputs = [] 20 | for song in songs: 21 | song_id, _, _, _, path, base_score, total_score, avg_mult, _, _, _, _ = song 22 | output_lines = ["Optimising, please wait..."] 23 | output_lines.append(f"Path: {path}") 24 | output_lines.append(f"No SP score: {base_score}") 25 | output_lines.append(f"Total score: {total_score}") 26 | if avg_mult: 27 | output_lines.append(f"Average multiplier: {avg_mult}x") 28 | c.execute( 29 | ( 30 | "select description, activation_end from activations " 31 | "where song_id = ? order by activation_number" 32 | ), 33 | (song_id,), 34 | ) 35 | for (description, act_end), activation in zip(c.fetchall(), path.split("-")): 36 | if act_end is None: 37 | output_lines.append(f"{activation}: {description}") 38 | else: 39 | output_lines.append(f"{activation}: {description} ({act_end})") 40 | output_lines.append("") 41 | outputs.append(os.linesep.join(output_lines).encode("utf-8")) 42 | 43 | conn.close() 44 | 45 | for song, output in zip(songs, outputs): 46 | _, file, difficulty, instrument, _, _, _, _, engine, sqz, ew, speed = song 47 | result = subprocess.run( 48 | [ 49 | program, 50 | "-f", 51 | f"{song_dir}/{file}", 52 | "-d", 53 | difficulty, 54 | "-i", 55 | instrument, 56 | "--engine", 57 | engine, 58 | "--speed", 59 | str(speed), 60 | "--squeeze", 61 | str(sqz), 62 | "--early-whammy", 63 | str(ew), 64 | ], 65 | capture_output=True, 66 | ) 67 | try: 68 | result.check_returncode() 69 | except subprocess.CalledProcessError: 70 | print(f"stdout: {result.stdout.decode('utf-8')}") 71 | print(f"stderr: {result.stderr.decode('utf-8')}") 72 | raise 73 | 74 | if result.stdout != output: 75 | print(f"Expected output: {output}") 76 | print(f"Actual output: {result.stdout}") 77 | raise RuntimeError(f"Song {file} has incorrect output") 78 | -------------------------------------------------------------------------------- /integration_tests/songs/bark-at-the-moon.mid: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/integration_tests/songs/bark-at-the-moon.mid -------------------------------------------------------------------------------- /integration_tests/songs/cheat-on-the-church.mid: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/integration_tests/songs/cheat-on-the-church.mid -------------------------------------------------------------------------------- /integration_tests/songs/cult-of-personality-gh3.mid: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/integration_tests/songs/cult-of-personality-gh3.mid -------------------------------------------------------------------------------- /integration_tests/songs/cups-and-cakes.mid: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/integration_tests/songs/cups-and-cakes.mid -------------------------------------------------------------------------------- /integration_tests/songs/epro.mid: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/integration_tests/songs/epro.mid -------------------------------------------------------------------------------- /integration_tests/songs/get-clean.mid: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/integration_tests/songs/get-clean.mid -------------------------------------------------------------------------------- /integration_tests/songs/green-grass-and-high-tides.mid: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/integration_tests/songs/green-grass-and-high-tides.mid -------------------------------------------------------------------------------- /integration_tests/songs/ignis-fatuus.mid: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/integration_tests/songs/ignis-fatuus.mid -------------------------------------------------------------------------------- /integration_tests/songs/lysios.chart: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/integration_tests/songs/lysios.chart -------------------------------------------------------------------------------- /integration_tests/songs/mississippi-queen-rb.mid: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/integration_tests/songs/mississippi-queen-rb.mid -------------------------------------------------------------------------------- /integration_tests/songs/raining-blood-sh.mid: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/integration_tests/songs/raining-blood-sh.mid -------------------------------------------------------------------------------- /integration_tests/songs/sweating-bullets.mid: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/integration_tests/songs/sweating-bullets.mid -------------------------------------------------------------------------------- /integration_tests/songs/timmy.mid: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/integration_tests/songs/timmy.mid -------------------------------------------------------------------------------- /integration_tests/tests.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/integration_tests/tests.db -------------------------------------------------------------------------------- /misc/How-to-read-paths.md: -------------------------------------------------------------------------------- 1 | # How to read paths 2 | 3 | ## Text notation 4 | 5 | A path is often abbreviated in the following form: 6 | 7 | 2-2(+1)-3 8 | 9 | CHOpt will produce this type of abbreviation. You read it as follows: 10 | 11 | 2 - Get two star power phrases before activating 12 | 13 | 2(+1) - Get two star power phrases before activating, and overlap one phrase 14 | during the activation 15 | 16 | 3 - Get three star power phrases before activating 17 | 18 | Sometimes a path ends in ES1, for example the optimal path for Fire It Up from 19 | Anti Hero 2 is 2(+1)-2(+1)-ES1. The ES1 means that you will acquire one star 20 | power phrase after the last activation, but you don't use it. It is 21 | theoretically possible for an optimal path to end in ES2, but I have never seen 22 | a non-artificial example. 23 | 24 | It is possible for a path to have you acquire 0 star power phrases before 25 | activating, if you can get enough whammy to activate. An extreme example is the 26 | optimal path for Time Traveler from Circuit Breaker, which is 27 | 3-1-0-0-0-2-1-0-0-0-2. It is also possible for a path to have you acquire 5 or 28 | more star power phrases before activating. One example is the optimal path for 29 | Higan Servant ~ One Conclusion from Anti Hero: Beach Episode, which is 30 | 5(+1)-2(+1). 31 | 32 | CHOpt will also give the start of every activation in text. NN means next note, 33 | otherwise it will count notes (1st G, 3rd RBO, and so on). If the activation is 34 | mid sustain it will specify roughly where to activate. This is currently very 35 | rudimentary though and it doesn't try if you do not get a phrase before an 36 | activation. 37 | 38 | ## Images 39 | 40 | CHOpt highlights regions of the song with the following colours: 41 | 42 | * blue: where star power should be active 43 | * green: where a star power phrase is 44 | * red: where to squeeze at the start and end of an activation 45 | * yellow: where the player should not gain star power from whammy 46 | * grey: solo section 47 | 48 | ### Squeezing 49 | 50 | The red shows how late/early you have to hit the first/late note of an 51 | activation. If you have to hit a note before the start early or a note after 52 | the end late, CHOpt will just draw star power up to those notes (suggestions to 53 | notate this cleanly are welcome). 54 | 55 | Sometimes a note during an activation must be hit early to ensure overlap, or a 56 | note must be hit late to avoid wasting star power on getting full bar. For an 57 | example of the latter, look at the Malevolence activation on Soulless 4 bass in 58 | [RileyTheFox's video](https://www.youtube.com/watch?v=MWl9mmx7kpY&t=6m). If the 59 | last note of that phrase is not hit late, CHelgaBot would max out on SP too 60 | early. This would cost star power and then it would be impossible to squeeze to 61 | the orange at the end. CHOpt will not draw this in any way but you may have to 62 | keep it in mind. 63 | 64 | ### Compressed whammy 65 | 66 | Yellow indicates where the player should stop acquiring star power from whammy. 67 | This is not quite the same as where the player should not whammy, because there 68 | is a delay from when the player stops whammying and when the game stops giving 69 | star power. I don't know exactly how this delay works, so it's up to you to 70 | adjust. 71 | 72 | The most common situation where the player should stop whammying is if it's 73 | optimal to get part of a star power sustain under star power, then get whammy 74 | from the rest. Typically this will entail whammying to extend the activation, 75 | stop whammying briefly, then start whammying once the activation ends. Be aware 76 | that in this situation the window of no whammy is often small enough that CHOpt 77 | will not draw it. 78 | 79 | Sometimes longer stretches of no whammying is required to prevent an unwanted 80 | overlap. A good example of this is afforded by the optimal path for More Than A 81 | Feeling from the Guitar Hero 1 rip. It is necessary to forego the whammy before 82 | the m76 activation because the whammy starting on m80 is needed to get maximum 83 | ticks in the next activation. 84 | 85 | [GH1 More Than A Feeling path](more-than-a-feeling.png) 86 | -------------------------------------------------------------------------------- /misc/more-than-a-feeling.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/misc/more-than-a-feeling.png -------------------------------------------------------------------------------- /misc/setlist.ps1: -------------------------------------------------------------------------------- 1 | # The location of CHOpt.exe (specifically, the CLI version). This path must 2 | # include the CHOpt.exe at the end (or whatever is appropriate, if the exe has 3 | # been renamed). 4 | $CHOPT_PATH = 'path\to\CHOpt\here' 5 | # The location of the setlist you want to optimise 6 | $SETLIST_PATH = 'path\to\setlist\here' 7 | # The extra arguments (other than path and song location) you wish to pass to 8 | # CHOpt, for example "--sqz 50 --ew 40" for 50% squeeze/40% whammy 9 | $CHOPT_ARGS = '' 10 | # The name of the image file you want (e.g., sqz-50.png) 11 | $IMAGE_NAME = 'path.png' 12 | 13 | $files = Get-ChildItem -LiteralPath $SETLIST_PATH -Recurse | Where-Object { ! $_.PSIsContainer } 14 | $charts = $files | Where-Object { $_.Name -eq 'notes.chart' } | Select-Object Directory 15 | $mids = $files | Where-Object { $_.Name -eq 'notes.mid' } | Select-Object Directory 16 | 17 | # We do the notes.chart files before the notes.mid files because some songs 18 | # will have both, and my understanding is Clone Hero will prioritise the 19 | # notes.mid files in that situation. 20 | 21 | foreach ($chart in $charts) { 22 | $chartPath = Join-Path -Path $chart.Directory -ChildPath 'notes.chart' 23 | $imgPath = Join-Path -Path $chart.Directory -ChildPath $IMAGE_NAME 24 | $fullArgs = "-f `"$chartPath`" -o `"$imgPath`" $CHOPT_ARGS" 25 | Start-Process -NoNewWindow -Wait -FilePath $CHOPT_PATH -ArgumentList $fullArgs 26 | } 27 | 28 | foreach ($mid in $mids) { 29 | $midPath = Join-Path -Path $mid.Directory -ChildPath 'notes.mid' 30 | $imgPath = Join-Path -Path $mid.Directory -ChildPath $IMAGE_NAME 31 | $fullArgs = "-f `"$midPath`" -o `"$imgPath`" $CHOPT_ARGS" 32 | Start-Process -NoNewWindow -Wait -FilePath $CHOPT_PATH -ArgumentList $fullArgs 33 | } -------------------------------------------------------------------------------- /resources/CHOpt.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # This script is taken from https://doc.qt.io/qt-6/linux-deployment.html and its 3 | # purpose is to let CHOpt find the dynamic libraries included along with it. 4 | appname=$(basename "$0" | sed s,\.sh$,,) 5 | 6 | dirname=$(dirname "$0") 7 | tmp="${dirname#?}" 8 | 9 | if [ "${dirname%$tmp}" != "/" ]; then 10 | dirname=$PWD/$dirname 11 | fi 12 | LD_LIBRARY_PATH=$dirname/"libs" 13 | export LD_LIBRARY_PATH 14 | "$dirname/$appname" "$@" 15 | -------------------------------------------------------------------------------- /resources/chopt.exe.manifest: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | true 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /resources/choptgui.exe.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | true 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /resources/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/icon.ico -------------------------------------------------------------------------------- /resources/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/icon.png -------------------------------------------------------------------------------- /resources/icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /resources/resources.rc: -------------------------------------------------------------------------------- 1 | IDR_MAINFRAME ICON "icon.ico" 2 | 1 VERSIONINFO 3 | FILEVERSION 1,9,5,0 4 | PRODUCTVERSION 1,9,5,0 5 | { 6 | BLOCK "StringFileInfo" 7 | { 8 | BLOCK "080904B0" 9 | { 10 | VALUE "FileDescription", "CHOpt\0", 11 | VALUE "FileVersion", "1.9.5.0\0", 12 | VALUE "OriginalFilename", "CHOpt.exe\0", 13 | VALUE "ProductName", "CHOpt\0", 14 | VALUE "ProductVersion", "1.9.5.0\0" 15 | } 16 | } 17 | BLOCK "VarFileInfo" 18 | { 19 | VALUE "Translation", 0x0809, 1200 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /resources/sprites/lefty/circles/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/circles/1.png -------------------------------------------------------------------------------- /resources/sprites/lefty/circles/10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/circles/10.png -------------------------------------------------------------------------------- /resources/sprites/lefty/circles/11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/circles/11.png -------------------------------------------------------------------------------- /resources/sprites/lefty/circles/12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/circles/12.png -------------------------------------------------------------------------------- /resources/sprites/lefty/circles/13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/circles/13.png -------------------------------------------------------------------------------- /resources/sprites/lefty/circles/14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/circles/14.png -------------------------------------------------------------------------------- /resources/sprites/lefty/circles/15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/circles/15.png -------------------------------------------------------------------------------- /resources/sprites/lefty/circles/16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/circles/16.png -------------------------------------------------------------------------------- /resources/sprites/lefty/circles/17.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/circles/17.png -------------------------------------------------------------------------------- /resources/sprites/lefty/circles/18.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/circles/18.png -------------------------------------------------------------------------------- /resources/sprites/lefty/circles/19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/circles/19.png -------------------------------------------------------------------------------- /resources/sprites/lefty/circles/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/circles/2.png -------------------------------------------------------------------------------- /resources/sprites/lefty/circles/20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/circles/20.png -------------------------------------------------------------------------------- /resources/sprites/lefty/circles/21.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/circles/21.png -------------------------------------------------------------------------------- /resources/sprites/lefty/circles/22.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/circles/22.png -------------------------------------------------------------------------------- /resources/sprites/lefty/circles/23.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/circles/23.png -------------------------------------------------------------------------------- /resources/sprites/lefty/circles/24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/circles/24.png -------------------------------------------------------------------------------- /resources/sprites/lefty/circles/25.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/circles/25.png -------------------------------------------------------------------------------- /resources/sprites/lefty/circles/26.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/circles/26.png -------------------------------------------------------------------------------- /resources/sprites/lefty/circles/27.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/circles/27.png -------------------------------------------------------------------------------- /resources/sprites/lefty/circles/28.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/circles/28.png -------------------------------------------------------------------------------- /resources/sprites/lefty/circles/29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/circles/29.png -------------------------------------------------------------------------------- /resources/sprites/lefty/circles/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/circles/3.png -------------------------------------------------------------------------------- /resources/sprites/lefty/circles/30.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/circles/30.png -------------------------------------------------------------------------------- /resources/sprites/lefty/circles/31.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/circles/31.png -------------------------------------------------------------------------------- /resources/sprites/lefty/circles/32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/circles/32.png -------------------------------------------------------------------------------- /resources/sprites/lefty/circles/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/circles/4.png -------------------------------------------------------------------------------- /resources/sprites/lefty/circles/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/circles/5.png -------------------------------------------------------------------------------- /resources/sprites/lefty/circles/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/circles/6.png -------------------------------------------------------------------------------- /resources/sprites/lefty/circles/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/circles/7.png -------------------------------------------------------------------------------- /resources/sprites/lefty/circles/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/circles/8.png -------------------------------------------------------------------------------- /resources/sprites/lefty/circles/9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/circles/9.png -------------------------------------------------------------------------------- /resources/sprites/lefty/cymbals/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/cymbals/1.png -------------------------------------------------------------------------------- /resources/sprites/lefty/cymbals/16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/cymbals/16.png -------------------------------------------------------------------------------- /resources/sprites/lefty/cymbals/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/cymbals/2.png -------------------------------------------------------------------------------- /resources/sprites/lefty/cymbals/32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/cymbals/32.png -------------------------------------------------------------------------------- /resources/sprites/lefty/cymbals/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/cymbals/4.png -------------------------------------------------------------------------------- /resources/sprites/lefty/cymbals/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/cymbals/8.png -------------------------------------------------------------------------------- /resources/sprites/lefty/drums/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/drums/1.png -------------------------------------------------------------------------------- /resources/sprites/lefty/drums/16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/drums/16.png -------------------------------------------------------------------------------- /resources/sprites/lefty/drums/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/drums/2.png -------------------------------------------------------------------------------- /resources/sprites/lefty/drums/32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/drums/32.png -------------------------------------------------------------------------------- /resources/sprites/lefty/drums/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/drums/4.png -------------------------------------------------------------------------------- /resources/sprites/lefty/drums/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/drums/8.png -------------------------------------------------------------------------------- /resources/sprites/lefty/ghl/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/ghl/1.png -------------------------------------------------------------------------------- /resources/sprites/lefty/ghl/10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/ghl/10.png -------------------------------------------------------------------------------- /resources/sprites/lefty/ghl/11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/ghl/11.png -------------------------------------------------------------------------------- /resources/sprites/lefty/ghl/12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/ghl/12.png -------------------------------------------------------------------------------- /resources/sprites/lefty/ghl/13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/ghl/13.png -------------------------------------------------------------------------------- /resources/sprites/lefty/ghl/14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/ghl/14.png -------------------------------------------------------------------------------- /resources/sprites/lefty/ghl/15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/ghl/15.png -------------------------------------------------------------------------------- /resources/sprites/lefty/ghl/16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/ghl/16.png -------------------------------------------------------------------------------- /resources/sprites/lefty/ghl/17.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/ghl/17.png -------------------------------------------------------------------------------- /resources/sprites/lefty/ghl/18.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/ghl/18.png -------------------------------------------------------------------------------- /resources/sprites/lefty/ghl/19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/ghl/19.png -------------------------------------------------------------------------------- /resources/sprites/lefty/ghl/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/ghl/2.png -------------------------------------------------------------------------------- /resources/sprites/lefty/ghl/20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/ghl/20.png -------------------------------------------------------------------------------- /resources/sprites/lefty/ghl/21.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/ghl/21.png -------------------------------------------------------------------------------- /resources/sprites/lefty/ghl/22.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/ghl/22.png -------------------------------------------------------------------------------- /resources/sprites/lefty/ghl/23.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/ghl/23.png -------------------------------------------------------------------------------- /resources/sprites/lefty/ghl/24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/ghl/24.png -------------------------------------------------------------------------------- /resources/sprites/lefty/ghl/25.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/ghl/25.png -------------------------------------------------------------------------------- /resources/sprites/lefty/ghl/26.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/ghl/26.png -------------------------------------------------------------------------------- /resources/sprites/lefty/ghl/27.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/ghl/27.png -------------------------------------------------------------------------------- /resources/sprites/lefty/ghl/28.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/ghl/28.png -------------------------------------------------------------------------------- /resources/sprites/lefty/ghl/29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/ghl/29.png -------------------------------------------------------------------------------- /resources/sprites/lefty/ghl/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/ghl/3.png -------------------------------------------------------------------------------- /resources/sprites/lefty/ghl/30.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/ghl/30.png -------------------------------------------------------------------------------- /resources/sprites/lefty/ghl/31.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/ghl/31.png -------------------------------------------------------------------------------- /resources/sprites/lefty/ghl/32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/ghl/32.png -------------------------------------------------------------------------------- /resources/sprites/lefty/ghl/33.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/ghl/33.png -------------------------------------------------------------------------------- /resources/sprites/lefty/ghl/34.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/ghl/34.png -------------------------------------------------------------------------------- /resources/sprites/lefty/ghl/35.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/ghl/35.png -------------------------------------------------------------------------------- /resources/sprites/lefty/ghl/36.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/ghl/36.png -------------------------------------------------------------------------------- /resources/sprites/lefty/ghl/37.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/ghl/37.png -------------------------------------------------------------------------------- /resources/sprites/lefty/ghl/38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/ghl/38.png -------------------------------------------------------------------------------- /resources/sprites/lefty/ghl/39.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/ghl/39.png -------------------------------------------------------------------------------- /resources/sprites/lefty/ghl/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/ghl/4.png -------------------------------------------------------------------------------- /resources/sprites/lefty/ghl/40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/ghl/40.png -------------------------------------------------------------------------------- /resources/sprites/lefty/ghl/41.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/ghl/41.png -------------------------------------------------------------------------------- /resources/sprites/lefty/ghl/42.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/ghl/42.png -------------------------------------------------------------------------------- /resources/sprites/lefty/ghl/43.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/ghl/43.png -------------------------------------------------------------------------------- /resources/sprites/lefty/ghl/44.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/ghl/44.png -------------------------------------------------------------------------------- /resources/sprites/lefty/ghl/45.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/ghl/45.png -------------------------------------------------------------------------------- /resources/sprites/lefty/ghl/46.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/ghl/46.png -------------------------------------------------------------------------------- /resources/sprites/lefty/ghl/47.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/ghl/47.png -------------------------------------------------------------------------------- /resources/sprites/lefty/ghl/48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/ghl/48.png -------------------------------------------------------------------------------- /resources/sprites/lefty/ghl/49.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/ghl/49.png -------------------------------------------------------------------------------- /resources/sprites/lefty/ghl/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/ghl/5.png -------------------------------------------------------------------------------- /resources/sprites/lefty/ghl/50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/ghl/50.png -------------------------------------------------------------------------------- /resources/sprites/lefty/ghl/51.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/ghl/51.png -------------------------------------------------------------------------------- /resources/sprites/lefty/ghl/52.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/ghl/52.png -------------------------------------------------------------------------------- /resources/sprites/lefty/ghl/53.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/ghl/53.png -------------------------------------------------------------------------------- /resources/sprites/lefty/ghl/54.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/ghl/54.png -------------------------------------------------------------------------------- /resources/sprites/lefty/ghl/55.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/ghl/55.png -------------------------------------------------------------------------------- /resources/sprites/lefty/ghl/56.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/ghl/56.png -------------------------------------------------------------------------------- /resources/sprites/lefty/ghl/57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/ghl/57.png -------------------------------------------------------------------------------- /resources/sprites/lefty/ghl/58.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/ghl/58.png -------------------------------------------------------------------------------- /resources/sprites/lefty/ghl/59.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/ghl/59.png -------------------------------------------------------------------------------- /resources/sprites/lefty/ghl/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/ghl/6.png -------------------------------------------------------------------------------- /resources/sprites/lefty/ghl/60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/ghl/60.png -------------------------------------------------------------------------------- /resources/sprites/lefty/ghl/61.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/ghl/61.png -------------------------------------------------------------------------------- /resources/sprites/lefty/ghl/62.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/ghl/62.png -------------------------------------------------------------------------------- /resources/sprites/lefty/ghl/63.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/ghl/63.png -------------------------------------------------------------------------------- /resources/sprites/lefty/ghl/64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/ghl/64.png -------------------------------------------------------------------------------- /resources/sprites/lefty/ghl/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/ghl/7.png -------------------------------------------------------------------------------- /resources/sprites/lefty/ghl/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/ghl/8.png -------------------------------------------------------------------------------- /resources/sprites/lefty/ghl/9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/ghl/9.png -------------------------------------------------------------------------------- /resources/sprites/lefty/stars/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/stars/1.png -------------------------------------------------------------------------------- /resources/sprites/lefty/stars/10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/stars/10.png -------------------------------------------------------------------------------- /resources/sprites/lefty/stars/11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/stars/11.png -------------------------------------------------------------------------------- /resources/sprites/lefty/stars/12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/stars/12.png -------------------------------------------------------------------------------- /resources/sprites/lefty/stars/13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/stars/13.png -------------------------------------------------------------------------------- /resources/sprites/lefty/stars/14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/stars/14.png -------------------------------------------------------------------------------- /resources/sprites/lefty/stars/15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/stars/15.png -------------------------------------------------------------------------------- /resources/sprites/lefty/stars/16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/stars/16.png -------------------------------------------------------------------------------- /resources/sprites/lefty/stars/17.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/stars/17.png -------------------------------------------------------------------------------- /resources/sprites/lefty/stars/18.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/stars/18.png -------------------------------------------------------------------------------- /resources/sprites/lefty/stars/19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/stars/19.png -------------------------------------------------------------------------------- /resources/sprites/lefty/stars/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/stars/2.png -------------------------------------------------------------------------------- /resources/sprites/lefty/stars/20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/stars/20.png -------------------------------------------------------------------------------- /resources/sprites/lefty/stars/21.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/stars/21.png -------------------------------------------------------------------------------- /resources/sprites/lefty/stars/22.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/stars/22.png -------------------------------------------------------------------------------- /resources/sprites/lefty/stars/23.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/stars/23.png -------------------------------------------------------------------------------- /resources/sprites/lefty/stars/24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/stars/24.png -------------------------------------------------------------------------------- /resources/sprites/lefty/stars/25.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/stars/25.png -------------------------------------------------------------------------------- /resources/sprites/lefty/stars/26.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/stars/26.png -------------------------------------------------------------------------------- /resources/sprites/lefty/stars/27.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/stars/27.png -------------------------------------------------------------------------------- /resources/sprites/lefty/stars/28.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/stars/28.png -------------------------------------------------------------------------------- /resources/sprites/lefty/stars/29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/stars/29.png -------------------------------------------------------------------------------- /resources/sprites/lefty/stars/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/stars/3.png -------------------------------------------------------------------------------- /resources/sprites/lefty/stars/30.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/stars/30.png -------------------------------------------------------------------------------- /resources/sprites/lefty/stars/31.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/stars/31.png -------------------------------------------------------------------------------- /resources/sprites/lefty/stars/32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/stars/32.png -------------------------------------------------------------------------------- /resources/sprites/lefty/stars/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/stars/4.png -------------------------------------------------------------------------------- /resources/sprites/lefty/stars/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/stars/5.png -------------------------------------------------------------------------------- /resources/sprites/lefty/stars/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/stars/6.png -------------------------------------------------------------------------------- /resources/sprites/lefty/stars/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/stars/7.png -------------------------------------------------------------------------------- /resources/sprites/lefty/stars/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/stars/8.png -------------------------------------------------------------------------------- /resources/sprites/lefty/stars/9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/lefty/stars/9.png -------------------------------------------------------------------------------- /resources/sprites/righty/circles/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/circles/1.png -------------------------------------------------------------------------------- /resources/sprites/righty/circles/10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/circles/10.png -------------------------------------------------------------------------------- /resources/sprites/righty/circles/11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/circles/11.png -------------------------------------------------------------------------------- /resources/sprites/righty/circles/12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/circles/12.png -------------------------------------------------------------------------------- /resources/sprites/righty/circles/13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/circles/13.png -------------------------------------------------------------------------------- /resources/sprites/righty/circles/14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/circles/14.png -------------------------------------------------------------------------------- /resources/sprites/righty/circles/15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/circles/15.png -------------------------------------------------------------------------------- /resources/sprites/righty/circles/16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/circles/16.png -------------------------------------------------------------------------------- /resources/sprites/righty/circles/17.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/circles/17.png -------------------------------------------------------------------------------- /resources/sprites/righty/circles/18.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/circles/18.png -------------------------------------------------------------------------------- /resources/sprites/righty/circles/19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/circles/19.png -------------------------------------------------------------------------------- /resources/sprites/righty/circles/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/circles/2.png -------------------------------------------------------------------------------- /resources/sprites/righty/circles/20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/circles/20.png -------------------------------------------------------------------------------- /resources/sprites/righty/circles/21.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/circles/21.png -------------------------------------------------------------------------------- /resources/sprites/righty/circles/22.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/circles/22.png -------------------------------------------------------------------------------- /resources/sprites/righty/circles/23.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/circles/23.png -------------------------------------------------------------------------------- /resources/sprites/righty/circles/24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/circles/24.png -------------------------------------------------------------------------------- /resources/sprites/righty/circles/25.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/circles/25.png -------------------------------------------------------------------------------- /resources/sprites/righty/circles/26.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/circles/26.png -------------------------------------------------------------------------------- /resources/sprites/righty/circles/27.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/circles/27.png -------------------------------------------------------------------------------- /resources/sprites/righty/circles/28.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/circles/28.png -------------------------------------------------------------------------------- /resources/sprites/righty/circles/29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/circles/29.png -------------------------------------------------------------------------------- /resources/sprites/righty/circles/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/circles/3.png -------------------------------------------------------------------------------- /resources/sprites/righty/circles/30.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/circles/30.png -------------------------------------------------------------------------------- /resources/sprites/righty/circles/31.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/circles/31.png -------------------------------------------------------------------------------- /resources/sprites/righty/circles/32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/circles/32.png -------------------------------------------------------------------------------- /resources/sprites/righty/circles/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/circles/4.png -------------------------------------------------------------------------------- /resources/sprites/righty/circles/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/circles/5.png -------------------------------------------------------------------------------- /resources/sprites/righty/circles/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/circles/6.png -------------------------------------------------------------------------------- /resources/sprites/righty/circles/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/circles/7.png -------------------------------------------------------------------------------- /resources/sprites/righty/circles/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/circles/8.png -------------------------------------------------------------------------------- /resources/sprites/righty/circles/9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/circles/9.png -------------------------------------------------------------------------------- /resources/sprites/righty/cymbals/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/cymbals/1.png -------------------------------------------------------------------------------- /resources/sprites/righty/cymbals/16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/cymbals/16.png -------------------------------------------------------------------------------- /resources/sprites/righty/cymbals/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/cymbals/2.png -------------------------------------------------------------------------------- /resources/sprites/righty/cymbals/32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/cymbals/32.png -------------------------------------------------------------------------------- /resources/sprites/righty/cymbals/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/cymbals/4.png -------------------------------------------------------------------------------- /resources/sprites/righty/cymbals/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/cymbals/8.png -------------------------------------------------------------------------------- /resources/sprites/righty/drums/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/drums/1.png -------------------------------------------------------------------------------- /resources/sprites/righty/drums/16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/drums/16.png -------------------------------------------------------------------------------- /resources/sprites/righty/drums/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/drums/2.png -------------------------------------------------------------------------------- /resources/sprites/righty/drums/32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/drums/32.png -------------------------------------------------------------------------------- /resources/sprites/righty/drums/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/drums/4.png -------------------------------------------------------------------------------- /resources/sprites/righty/drums/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/drums/8.png -------------------------------------------------------------------------------- /resources/sprites/righty/ghl/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/ghl/1.png -------------------------------------------------------------------------------- /resources/sprites/righty/ghl/10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/ghl/10.png -------------------------------------------------------------------------------- /resources/sprites/righty/ghl/11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/ghl/11.png -------------------------------------------------------------------------------- /resources/sprites/righty/ghl/12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/ghl/12.png -------------------------------------------------------------------------------- /resources/sprites/righty/ghl/13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/ghl/13.png -------------------------------------------------------------------------------- /resources/sprites/righty/ghl/14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/ghl/14.png -------------------------------------------------------------------------------- /resources/sprites/righty/ghl/15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/ghl/15.png -------------------------------------------------------------------------------- /resources/sprites/righty/ghl/16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/ghl/16.png -------------------------------------------------------------------------------- /resources/sprites/righty/ghl/17.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/ghl/17.png -------------------------------------------------------------------------------- /resources/sprites/righty/ghl/18.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/ghl/18.png -------------------------------------------------------------------------------- /resources/sprites/righty/ghl/19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/ghl/19.png -------------------------------------------------------------------------------- /resources/sprites/righty/ghl/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/ghl/2.png -------------------------------------------------------------------------------- /resources/sprites/righty/ghl/20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/ghl/20.png -------------------------------------------------------------------------------- /resources/sprites/righty/ghl/21.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/ghl/21.png -------------------------------------------------------------------------------- /resources/sprites/righty/ghl/22.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/ghl/22.png -------------------------------------------------------------------------------- /resources/sprites/righty/ghl/23.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/ghl/23.png -------------------------------------------------------------------------------- /resources/sprites/righty/ghl/24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/ghl/24.png -------------------------------------------------------------------------------- /resources/sprites/righty/ghl/25.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/ghl/25.png -------------------------------------------------------------------------------- /resources/sprites/righty/ghl/26.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/ghl/26.png -------------------------------------------------------------------------------- /resources/sprites/righty/ghl/27.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/ghl/27.png -------------------------------------------------------------------------------- /resources/sprites/righty/ghl/28.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/ghl/28.png -------------------------------------------------------------------------------- /resources/sprites/righty/ghl/29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/ghl/29.png -------------------------------------------------------------------------------- /resources/sprites/righty/ghl/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/ghl/3.png -------------------------------------------------------------------------------- /resources/sprites/righty/ghl/30.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/ghl/30.png -------------------------------------------------------------------------------- /resources/sprites/righty/ghl/31.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/ghl/31.png -------------------------------------------------------------------------------- /resources/sprites/righty/ghl/32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/ghl/32.png -------------------------------------------------------------------------------- /resources/sprites/righty/ghl/33.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/ghl/33.png -------------------------------------------------------------------------------- /resources/sprites/righty/ghl/34.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/ghl/34.png -------------------------------------------------------------------------------- /resources/sprites/righty/ghl/35.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/ghl/35.png -------------------------------------------------------------------------------- /resources/sprites/righty/ghl/36.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/ghl/36.png -------------------------------------------------------------------------------- /resources/sprites/righty/ghl/37.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/ghl/37.png -------------------------------------------------------------------------------- /resources/sprites/righty/ghl/38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/ghl/38.png -------------------------------------------------------------------------------- /resources/sprites/righty/ghl/39.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/ghl/39.png -------------------------------------------------------------------------------- /resources/sprites/righty/ghl/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/ghl/4.png -------------------------------------------------------------------------------- /resources/sprites/righty/ghl/40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/ghl/40.png -------------------------------------------------------------------------------- /resources/sprites/righty/ghl/41.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/ghl/41.png -------------------------------------------------------------------------------- /resources/sprites/righty/ghl/42.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/ghl/42.png -------------------------------------------------------------------------------- /resources/sprites/righty/ghl/43.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/ghl/43.png -------------------------------------------------------------------------------- /resources/sprites/righty/ghl/44.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/ghl/44.png -------------------------------------------------------------------------------- /resources/sprites/righty/ghl/45.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/ghl/45.png -------------------------------------------------------------------------------- /resources/sprites/righty/ghl/46.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/ghl/46.png -------------------------------------------------------------------------------- /resources/sprites/righty/ghl/47.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/ghl/47.png -------------------------------------------------------------------------------- /resources/sprites/righty/ghl/48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/ghl/48.png -------------------------------------------------------------------------------- /resources/sprites/righty/ghl/49.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/ghl/49.png -------------------------------------------------------------------------------- /resources/sprites/righty/ghl/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/ghl/5.png -------------------------------------------------------------------------------- /resources/sprites/righty/ghl/50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/ghl/50.png -------------------------------------------------------------------------------- /resources/sprites/righty/ghl/51.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/ghl/51.png -------------------------------------------------------------------------------- /resources/sprites/righty/ghl/52.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/ghl/52.png -------------------------------------------------------------------------------- /resources/sprites/righty/ghl/53.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/ghl/53.png -------------------------------------------------------------------------------- /resources/sprites/righty/ghl/54.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/ghl/54.png -------------------------------------------------------------------------------- /resources/sprites/righty/ghl/55.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/ghl/55.png -------------------------------------------------------------------------------- /resources/sprites/righty/ghl/56.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/ghl/56.png -------------------------------------------------------------------------------- /resources/sprites/righty/ghl/57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/ghl/57.png -------------------------------------------------------------------------------- /resources/sprites/righty/ghl/58.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/ghl/58.png -------------------------------------------------------------------------------- /resources/sprites/righty/ghl/59.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/ghl/59.png -------------------------------------------------------------------------------- /resources/sprites/righty/ghl/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/ghl/6.png -------------------------------------------------------------------------------- /resources/sprites/righty/ghl/60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/ghl/60.png -------------------------------------------------------------------------------- /resources/sprites/righty/ghl/61.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/ghl/61.png -------------------------------------------------------------------------------- /resources/sprites/righty/ghl/62.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/ghl/62.png -------------------------------------------------------------------------------- /resources/sprites/righty/ghl/63.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/ghl/63.png -------------------------------------------------------------------------------- /resources/sprites/righty/ghl/64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/ghl/64.png -------------------------------------------------------------------------------- /resources/sprites/righty/ghl/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/ghl/7.png -------------------------------------------------------------------------------- /resources/sprites/righty/ghl/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/ghl/8.png -------------------------------------------------------------------------------- /resources/sprites/righty/ghl/9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/ghl/9.png -------------------------------------------------------------------------------- /resources/sprites/righty/stars/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/stars/1.png -------------------------------------------------------------------------------- /resources/sprites/righty/stars/10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/stars/10.png -------------------------------------------------------------------------------- /resources/sprites/righty/stars/11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/stars/11.png -------------------------------------------------------------------------------- /resources/sprites/righty/stars/12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/stars/12.png -------------------------------------------------------------------------------- /resources/sprites/righty/stars/13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/stars/13.png -------------------------------------------------------------------------------- /resources/sprites/righty/stars/14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/stars/14.png -------------------------------------------------------------------------------- /resources/sprites/righty/stars/15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/stars/15.png -------------------------------------------------------------------------------- /resources/sprites/righty/stars/16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/stars/16.png -------------------------------------------------------------------------------- /resources/sprites/righty/stars/17.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/stars/17.png -------------------------------------------------------------------------------- /resources/sprites/righty/stars/18.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/stars/18.png -------------------------------------------------------------------------------- /resources/sprites/righty/stars/19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/stars/19.png -------------------------------------------------------------------------------- /resources/sprites/righty/stars/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/stars/2.png -------------------------------------------------------------------------------- /resources/sprites/righty/stars/20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/stars/20.png -------------------------------------------------------------------------------- /resources/sprites/righty/stars/21.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/stars/21.png -------------------------------------------------------------------------------- /resources/sprites/righty/stars/22.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/stars/22.png -------------------------------------------------------------------------------- /resources/sprites/righty/stars/23.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/stars/23.png -------------------------------------------------------------------------------- /resources/sprites/righty/stars/24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/stars/24.png -------------------------------------------------------------------------------- /resources/sprites/righty/stars/25.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/stars/25.png -------------------------------------------------------------------------------- /resources/sprites/righty/stars/26.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/stars/26.png -------------------------------------------------------------------------------- /resources/sprites/righty/stars/27.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/stars/27.png -------------------------------------------------------------------------------- /resources/sprites/righty/stars/28.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/stars/28.png -------------------------------------------------------------------------------- /resources/sprites/righty/stars/29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/stars/29.png -------------------------------------------------------------------------------- /resources/sprites/righty/stars/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/stars/3.png -------------------------------------------------------------------------------- /resources/sprites/righty/stars/30.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/stars/30.png -------------------------------------------------------------------------------- /resources/sprites/righty/stars/31.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/stars/31.png -------------------------------------------------------------------------------- /resources/sprites/righty/stars/32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/stars/32.png -------------------------------------------------------------------------------- /resources/sprites/righty/stars/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/stars/4.png -------------------------------------------------------------------------------- /resources/sprites/righty/stars/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/stars/5.png -------------------------------------------------------------------------------- /resources/sprites/righty/stars/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/stars/6.png -------------------------------------------------------------------------------- /resources/sprites/righty/stars/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/stars/7.png -------------------------------------------------------------------------------- /resources/sprites/righty/stars/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/stars/8.png -------------------------------------------------------------------------------- /resources/sprites/righty/stars/9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GenericMadScientist/CHOpt/e4cf40665958e3d3f77f2fde7d54081bdc6a4036/resources/sprites/righty/stars/9.png -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [flake8] 2 | max-line-length = 88 3 | extend-ignore = E203 4 | -------------------------------------------------------------------------------- /src/ini.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * CHOpt - Star Power optimiser for Clone Hero 3 | * Copyright (C) 2020, 2021, 2023 Raymond Wright 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include 20 | #include 21 | 22 | #include "ini.hpp" 23 | #include "stringutil.hpp" 24 | 25 | SightRead::Metadata parse_ini(std::string_view data) 26 | { 27 | constexpr auto ARTIST_SIZE = 6; 28 | constexpr auto CHARTER_SIZE = 7; 29 | constexpr auto FRETS_SIZE = 5; 30 | constexpr auto NAME_SIZE = 4; 31 | 32 | std::string u8_string = to_utf8_string(data); 33 | data = u8_string; 34 | 35 | SightRead::Metadata metadata; 36 | metadata.name = "Unknown Song"; 37 | metadata.artist = "Unknown Artist"; 38 | metadata.charter = "Unknown Charter"; 39 | while (!data.empty()) { 40 | const auto line = break_off_newline(data); 41 | if (line.starts_with("name")) { 42 | auto value = skip_whitespace(line.substr(NAME_SIZE)); 43 | if (value[0] != '=') { 44 | continue; 45 | } 46 | value = skip_whitespace(value.substr(1)); 47 | metadata.name = value; 48 | } else if (line.starts_with("artist")) { 49 | auto value = skip_whitespace(line.substr(ARTIST_SIZE)); 50 | if (value[0] != '=') { 51 | continue; 52 | } 53 | value = skip_whitespace(value.substr(1)); 54 | metadata.artist = value; 55 | } else if (line.starts_with("charter")) { 56 | auto value = skip_whitespace(line.substr(CHARTER_SIZE)); 57 | if (value[0] != '=') { 58 | continue; 59 | } 60 | value = skip_whitespace(value.substr(1)); 61 | if (!value.empty()) { 62 | metadata.charter = value; 63 | } 64 | } else if (line.starts_with("frets")) { 65 | auto value = skip_whitespace(line.substr(FRETS_SIZE)); 66 | if (value[0] != '=') { 67 | continue; 68 | } 69 | value = skip_whitespace(value.substr(1)); 70 | if (!value.empty()) { 71 | metadata.charter = value; 72 | } 73 | } 74 | } 75 | 76 | return metadata; 77 | } 78 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * CHOpt - Star Power optimiser for Clone Hero 3 | * Copyright (C) 2020, 2021, 2022, 2023, 2024, 2025 Raymond Wright 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | #include 24 | #include 25 | 26 | #include 27 | 28 | #include "image.hpp" 29 | #include "optimiser.hpp" 30 | #include "settings.hpp" 31 | #include "songfile.hpp" 32 | 33 | int main(int argc, char** argv) 34 | { 35 | QTextStream q_stdout(stdout); 36 | QTextStream q_stderr(stderr); 37 | 38 | try { 39 | QCoreApplication app {argc, argv}; 40 | QCoreApplication::setApplicationName("CHOpt"); 41 | QCoreApplication::setApplicationVersion("1.9.5"); 42 | 43 | const auto settings = from_args(QCoreApplication::arguments()); 44 | const SongFile song_file {settings.filename}; 45 | auto song = song_file.load_song(settings.game); 46 | const auto& track 47 | = song.track(settings.instrument, settings.difficulty); 48 | const std::atomic terminate {false}; 49 | const auto builder = make_builder( 50 | song, track, settings, [&](auto p) { q_stdout << p << '\n'; }, 51 | &terminate); 52 | q_stdout.flush(); 53 | if (settings.draw_image) { 54 | const Image image {builder}; 55 | image.save(settings.image_path.c_str()); 56 | } 57 | return EXIT_SUCCESS; 58 | } catch (const std::exception& e) { 59 | q_stderr << "Error: " << e.what() << '\n'; 60 | return EXIT_FAILURE; 61 | } catch (...) { 62 | q_stderr << "Unexpected non-exception error!" << '\n'; 63 | return EXIT_FAILURE; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/songfile.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * CHOpt - Star Power optimiser for Clone Hero 3 | * Copyright (C) 2023, 2024 Raymond Wright 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | #include 24 | #include 25 | 26 | #include 27 | #include 28 | 29 | #include "ini.hpp" 30 | #include "songfile.hpp" 31 | #include "stringutil.hpp" 32 | 33 | namespace { 34 | std::set permitted_instruments(Game game) 35 | { 36 | switch (game) { 37 | case Game::CloneHero: 38 | return {SightRead::Instrument::Guitar, 39 | SightRead::Instrument::GuitarCoop, 40 | SightRead::Instrument::Bass, 41 | SightRead::Instrument::Rhythm, 42 | SightRead::Instrument::Keys, 43 | SightRead::Instrument::GHLGuitar, 44 | SightRead::Instrument::GHLBass, 45 | SightRead::Instrument::GHLRhythm, 46 | SightRead::Instrument::GHLGuitarCoop, 47 | SightRead::Instrument::Drums}; 48 | case Game::FortniteFestival: 49 | return {SightRead::Instrument::FortniteGuitar, 50 | SightRead::Instrument::FortniteBass, 51 | SightRead::Instrument::FortniteDrums, 52 | SightRead::Instrument::FortniteVocals, 53 | SightRead::Instrument::FortniteProGuitar, 54 | SightRead::Instrument::FortniteProBass}; 55 | case Game::GuitarHeroOne: 56 | return {SightRead::Instrument::Guitar}; 57 | case Game::RockBand: 58 | return {SightRead::Instrument::Guitar, SightRead::Instrument::Bass}; 59 | case Game::RockBandThree: 60 | return {SightRead::Instrument::Guitar, SightRead::Instrument::Bass, 61 | SightRead::Instrument::Keys}; 62 | default: 63 | throw std::invalid_argument("Invalid Game"); 64 | } 65 | } 66 | 67 | bool parse_solos(Game game) 68 | { 69 | return game != Game::GuitarHeroOne && game != Game::FortniteFestival; 70 | } 71 | } 72 | 73 | SongFile::SongFile(const std::string& filename) 74 | { 75 | std::string ini_file; 76 | const std::filesystem::path song_path {filename}; 77 | const auto song_directory = song_path.parent_path(); 78 | const auto ini_path = song_directory / "song.ini"; 79 | QFile ini {QString::fromStdString(ini_path.string())}; 80 | if (ini.open(QIODevice::ReadOnly | QIODevice::Text)) { 81 | ini_file = ini.readAll().toStdString(); 82 | } 83 | m_metadata = parse_ini(ini_file); 84 | 85 | if (filename.ends_with(".chart")) { 86 | m_file_type = FileType::Chart; 87 | } else if (filename.ends_with(".mid")) { 88 | m_file_type = FileType::Midi; 89 | } else { 90 | throw std::invalid_argument("file should be .chart or .mid"); 91 | } 92 | 93 | QFile chart {QString::fromStdString(filename)}; 94 | if (!chart.open(QIODevice::ReadOnly)) { 95 | throw std::invalid_argument("File did not open"); 96 | } 97 | const auto chart_buffer = chart.readAll(); 98 | m_loaded_file = std::vector {chart_buffer.cbegin(), 99 | chart_buffer.cend()}; 100 | } 101 | 102 | SightRead::Song SongFile::load_song(Game game) const 103 | { 104 | switch (m_file_type) { 105 | case FileType::Chart: { 106 | std::string_view chart_buffer { 107 | reinterpret_cast(m_loaded_file.data()), // NOLINT 108 | m_loaded_file.size()}; 109 | std::string u8_string; 110 | 111 | try { 112 | u8_string = to_utf8_string(chart_buffer); 113 | } catch (const std::invalid_argument& e) { 114 | throw SightRead::ParseError(e.what()); 115 | } 116 | SightRead::ChartParser parser {m_metadata}; 117 | parser.permit_instruments(permitted_instruments(game)); 118 | parser.parse_solos(parse_solos(game)); 119 | return parser.parse(u8_string); 120 | } 121 | case FileType::Midi: 122 | std::span midi_buffer {m_loaded_file.data(), 123 | m_loaded_file.size()}; 124 | SightRead::MidiParser parser {m_metadata}; 125 | parser.permit_instruments(permitted_instruments(game)); 126 | parser.parse_solos(parse_solos(game)); 127 | return parser.parse(midi_buffer); 128 | } 129 | throw std::runtime_error("Invalid file type"); 130 | } 131 | -------------------------------------------------------------------------------- /src/sptimemap.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * CHOpt - Star Power optimiser for Clone Hero 3 | * Copyright (C) 2023 Raymond Wright 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include 20 | 21 | #include "sptimemap.hpp" 22 | 23 | SightRead::Beat SpTimeMap::to_beats(SightRead::Second seconds) const 24 | { 25 | return m_tempo_map.to_beats(seconds); 26 | } 27 | 28 | SightRead::Beat SpTimeMap::to_beats(SpMeasure measures) const 29 | { 30 | switch (m_sp_mode) { 31 | case SpMode::Measure: 32 | return m_tempo_map.to_beats(SightRead::Measure {measures.value()}); 33 | case SpMode::OdBeat: 34 | return m_tempo_map.to_beats(SightRead::OdBeat {measures.value()}); 35 | default: 36 | throw std::runtime_error("Invalid SpMode value"); 37 | } 38 | } 39 | 40 | SightRead::Beat SpTimeMap::to_beats(SightRead::Tick ticks) const 41 | { 42 | return m_tempo_map.to_beats(ticks); 43 | } 44 | 45 | SightRead::Second SpTimeMap::to_seconds(SightRead::Beat beats) const 46 | { 47 | return m_tempo_map.to_seconds(beats); 48 | } 49 | 50 | SightRead::Second SpTimeMap::to_seconds(SpMeasure sp_measures) const 51 | { 52 | return to_seconds(to_beats(sp_measures)); 53 | } 54 | 55 | SpMeasure SpTimeMap::to_sp_measures(SightRead::Beat beats) const 56 | { 57 | switch (m_sp_mode) { 58 | case SpMode::Measure: 59 | return SpMeasure {m_tempo_map.to_measures(beats).value()}; 60 | case SpMode::OdBeat: 61 | return SpMeasure {m_tempo_map.to_od_beats(beats).value()}; 62 | default: 63 | throw std::runtime_error("Invalid SpMode value"); 64 | } 65 | } 66 | 67 | SpMeasure SpTimeMap::to_sp_measures(SightRead::Second seconds) const 68 | { 69 | return to_sp_measures(m_tempo_map.to_beats(seconds)); 70 | } -------------------------------------------------------------------------------- /src/stringutil.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * CHOpt - Star Power optimiser for Clone Hero 3 | * Copyright (C) 2020, 2021, 2022, 2023, 2024 Raymond Wright 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include 30 | 31 | #include "stringutil.hpp" 32 | 33 | std::string_view break_off_newline(std::string_view& input) 34 | { 35 | if (input.empty()) { 36 | throw SightRead::ParseError("No lines left"); 37 | } 38 | 39 | const auto newline_location 40 | = std::min(input.find('\n'), input.find("\r\n")); 41 | if (newline_location == std::string_view::npos) { 42 | const auto line = input; 43 | input.remove_prefix(input.size()); 44 | return line; 45 | } 46 | 47 | const auto line = input.substr(0, newline_location); 48 | input.remove_prefix(newline_location); 49 | input = skip_whitespace(input); 50 | return line; 51 | } 52 | 53 | std::string_view skip_whitespace(std::string_view input) 54 | { 55 | const auto first_non_ws_location = input.find_first_not_of(" \f\n\r\t\v"); 56 | input.remove_prefix(std::min(first_non_ws_location, input.size())); 57 | return input; 58 | } 59 | 60 | std::string to_ordinal(int ordinal) 61 | { 62 | constexpr int TENS_MODULUS = 10; 63 | constexpr int HUNDREDS_MODULUS = 100; 64 | constexpr std::array EXCEPTIONAL_TEENS {11, 12, 13}; 65 | 66 | if (ordinal < 0) { 67 | throw std::runtime_error("ordinal was negative"); 68 | } 69 | if (std::find(EXCEPTIONAL_TEENS.cbegin(), EXCEPTIONAL_TEENS.cend(), 70 | ordinal % HUNDREDS_MODULUS) 71 | != EXCEPTIONAL_TEENS.cend()) { 72 | return std::to_string(ordinal) + "th"; 73 | } 74 | if (ordinal % TENS_MODULUS == 1) { 75 | return std::to_string(ordinal) + "st"; 76 | } 77 | if (ordinal % TENS_MODULUS == 2) { 78 | return std::to_string(ordinal) + "nd"; 79 | } 80 | if (ordinal % TENS_MODULUS == 3) { 81 | return std::to_string(ordinal) + "rd"; 82 | } 83 | return std::to_string(ordinal) + "th"; 84 | } 85 | 86 | std::string to_utf8_string(std::string_view input) 87 | { 88 | const QByteArrayView byte_view {input.data(), 89 | static_cast(input.size())}; 90 | 91 | QStringDecoder to_utf8 {QStringDecoder::Utf8}; 92 | QString str = to_utf8(byte_view); 93 | if (!to_utf8.hasError()) { 94 | return str.toStdString(); 95 | } 96 | 97 | QStringDecoder to_latin_1 {QStringDecoder::Latin1}; 98 | str = to_latin_1(byte_view); 99 | if (!to_latin_1.hasError() && !str.contains(QChar {0})) { 100 | return str.toStdString(); 101 | } 102 | 103 | QStringDecoder to_utf16_le {QStringDecoder::Utf16LE}; 104 | str = to_utf16_le(byte_view); 105 | if (!to_utf16_le.hasError()) { 106 | return str.toStdString(); 107 | } 108 | 109 | throw std::runtime_error("Unable to determine string encoding"); 110 | } 111 | -------------------------------------------------------------------------------- /tests/ini_unittest.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * CHOpt - Star Power optimiser for Clone Hero 3 | * Copyright (C) 2020, 2021, 2022 Raymond Wright 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include 20 | 21 | #include "ini.hpp" 22 | 23 | BOOST_AUTO_TEST_CASE(default_ini_values_are_correct) 24 | { 25 | const auto ini_values = parse_ini(""); 26 | 27 | BOOST_CHECK_EQUAL(ini_values.name, "Unknown Song"); 28 | BOOST_CHECK_EQUAL(ini_values.artist, "Unknown Artist"); 29 | BOOST_CHECK_EQUAL(ini_values.charter, "Unknown Charter"); 30 | } 31 | 32 | BOOST_AUTO_TEST_CASE(values_with_no_spaces_around_equals_are_read) 33 | { 34 | const char* text 35 | = "name=Dummy Song\nartist=Dummy Artist\ncharter=Dummy Charter"; 36 | 37 | const auto ini_values = parse_ini(text); 38 | 39 | BOOST_CHECK_EQUAL(ini_values.name, "Dummy Song"); 40 | BOOST_CHECK_EQUAL(ini_values.artist, "Dummy Artist"); 41 | BOOST_CHECK_EQUAL(ini_values.charter, "Dummy Charter"); 42 | } 43 | 44 | BOOST_AUTO_TEST_CASE(values_with_spaces_around_equals_are_read) 45 | { 46 | const char* text = "name = Dummy Song 2\nartist = Dummy Artist 2\ncharter " 47 | "= Dummy Charter 2"; 48 | 49 | const auto ini_values = parse_ini(text); 50 | 51 | BOOST_CHECK_EQUAL(ini_values.name, "Dummy Song 2"); 52 | BOOST_CHECK_EQUAL(ini_values.artist, "Dummy Artist 2"); 53 | BOOST_CHECK_EQUAL(ini_values.charter, "Dummy Charter 2"); 54 | } 55 | 56 | BOOST_AUTO_TEST_CASE(equals_must_be_the_character_after_the_key) 57 | { 58 | const char* text = "name_length=0\nartist_length=0\ncharter_length=0"; 59 | 60 | const auto ini_values = parse_ini(text); 61 | 62 | BOOST_CHECK_EQUAL(ini_values.name, "Unknown Song"); 63 | BOOST_CHECK_EQUAL(ini_values.artist, "Unknown Artist"); 64 | BOOST_CHECK_EQUAL(ini_values.charter, "Unknown Charter"); 65 | } 66 | 67 | BOOST_AUTO_TEST_CASE(frets_is_a_synonym_for_charter) 68 | { 69 | const char* text = "frets=GMS"; 70 | 71 | const auto ini_values = parse_ini(text); 72 | 73 | BOOST_CHECK_EQUAL(ini_values.charter, "GMS"); 74 | } 75 | 76 | BOOST_AUTO_TEST_CASE(blank_frets_after_charter_is_ignored) 77 | { 78 | const char* text = "charter = Haggis\nfrets = "; 79 | 80 | const auto ini_values = parse_ini(text); 81 | 82 | BOOST_CHECK_EQUAL(ini_values.charter, "Haggis"); 83 | } 84 | 85 | BOOST_AUTO_TEST_CASE(utf16le_inis_are_read_correctly) 86 | { 87 | const std::string text { 88 | "\xFF\xFE\x6E\x00\x61\x00\x6D\x00\x65\x00\x3D\x00\x54\x00\x65\x00\x73" 89 | "\x00\x74\x00", 90 | 20}; 91 | 92 | const auto ini_values = parse_ini(text); 93 | 94 | BOOST_CHECK_EQUAL(ini_values.name, "Test"); 95 | } 96 | -------------------------------------------------------------------------------- /tests/stringutil_unittest.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * CHOpt - Star Power optimiser for Clone Hero 3 | * Copyright (C) 2020, 2021, 2022, 2023, 2024 Raymond Wright 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include 20 | 21 | #include 22 | 23 | #include "stringutil.hpp" 24 | 25 | BOOST_AUTO_TEST_CASE(break_off_newline_works_correctly) 26 | { 27 | std::string_view data = "Hello\nThe\rre\r\nWorld!"; 28 | 29 | BOOST_CHECK_EQUAL(break_off_newline(data), "Hello"); 30 | BOOST_CHECK_EQUAL(data, "The\rre\r\nWorld!"); 31 | BOOST_CHECK_EQUAL(break_off_newline(data), "The\rre"); 32 | BOOST_CHECK_EQUAL(data, "World!"); 33 | } 34 | 35 | BOOST_AUTO_TEST_CASE(skip_whitespace_works_correctly) 36 | { 37 | BOOST_CHECK_EQUAL(skip_whitespace("Hello"), "Hello"); 38 | BOOST_CHECK_EQUAL(skip_whitespace(" Hello"), "Hello"); 39 | BOOST_CHECK_EQUAL(skip_whitespace("H ello"), "H ello"); 40 | } 41 | 42 | BOOST_AUTO_TEST_CASE(to_utf8_string_strips_utf8_bom) 43 | { 44 | const std::string text {"\xEF\xBB\xBF\x6E"}; 45 | 46 | BOOST_CHECK_EQUAL(to_utf8_string(text), "n"); 47 | } 48 | 49 | BOOST_AUTO_TEST_CASE(to_utf8_string_treats_no_bom_string_as_utf8) 50 | { 51 | const std::string text {"Hello"}; 52 | 53 | BOOST_CHECK_EQUAL(to_utf8_string(text), "Hello"); 54 | } 55 | 56 | BOOST_AUTO_TEST_CASE(to_utf8_string_correctly_converts_from_utf16le_to_utf8) 57 | { 58 | const std::string text { 59 | "\xFF\xFE\x6E\x00\x61\x00\x6D\x00\x65\x00\x3D\x00\x54\x00\x65\x00\x73" 60 | "\x00\x74\x00", 61 | 20}; 62 | 63 | BOOST_CHECK_EQUAL(to_utf8_string(text), "name=Test"); 64 | } 65 | 66 | BOOST_AUTO_TEST_CASE(to_utf8_string_correctly_converts_from_latin_1_strings) 67 | { 68 | const std::string text {"\xE9\x30"}; 69 | 70 | BOOST_CHECK_EQUAL(to_utf8_string(text), "é0"); 71 | } 72 | 73 | BOOST_AUTO_TEST_CASE(to_ordinal_works_correctly) 74 | { 75 | BOOST_CHECK_EQUAL(to_ordinal(0), "0th"); 76 | BOOST_CHECK_EQUAL(to_ordinal(1), "1st"); 77 | BOOST_CHECK_EQUAL(to_ordinal(2), "2nd"); 78 | BOOST_CHECK_EQUAL(to_ordinal(3), "3rd"); 79 | BOOST_CHECK_EQUAL(to_ordinal(4), "4th"); 80 | BOOST_CHECK_EQUAL(to_ordinal(11), "11th"); 81 | BOOST_CHECK_EQUAL(to_ordinal(12), "12th"); 82 | BOOST_CHECK_EQUAL(to_ordinal(13), "13th"); 83 | BOOST_CHECK_THROW([&] { return to_ordinal(-1); }(), std::exception); 84 | } 85 | -------------------------------------------------------------------------------- /tests/test_main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * CHOpt - Star Power optimiser for Clone Hero 3 | * Copyright (C) 2022 Raymond Wright 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #define BOOST_TEST_MODULE CHOpt Tests 20 | #include 21 | --------------------------------------------------------------------------------