├── .gitattributes ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.yml │ ├── config.yml │ └── feature_request.yml ├── PULL_REQUEST_TEMPLATE.md ├── dependabot.yaml └── workflows │ ├── pr_artifacts.yml │ ├── publish_cli.yml │ ├── publish_gui.yml │ ├── publish_gui_nightly.yml │ ├── publish_gui_release.yml │ └── publish_pr.yml ├── .gitignore ├── .gitmodules ├── LICENSE.txt ├── README.md ├── SCRIPTS.md ├── UndertaleModCli ├── CommandOptions │ ├── DumpOptions.cs │ ├── InfoOptions.cs │ ├── LoadOptions.cs │ ├── NewOptions.cs │ └── ReplaceOptions.cs ├── Program.UMTLibInherited.cs ├── Program.cs ├── UndertaleModCli.csproj └── runtimeconfig.json ├── UndertaleModLib ├── Compiler │ ├── BuiltinList.cs │ ├── CodeBuilder.cs │ ├── CodeImportGroup.cs │ ├── CompileGroup.cs │ ├── CompileResult.cs │ └── ICodeImportOperation.cs ├── Decompiler │ ├── Assembler.cs │ ├── Disassembler.cs │ ├── GameSpecificResolver.cs │ └── GlobalDecompileContext.cs ├── FodyWeavers.xml ├── FodyWeavers.xsd ├── GameSpecificData │ ├── Definitions │ │ ├── deltarune.json │ │ ├── gamemaker.json │ │ └── undertale.json │ ├── README.txt │ └── Underanalyzer │ │ ├── deltarune.json │ │ ├── gamemaker.json │ │ ├── template.json │ │ └── undertale.json ├── Models │ ├── UndertaleAnimationCurve.cs │ ├── UndertaleAudioGroup.cs │ ├── UndertaleBackground.cs │ ├── UndertaleCode.cs │ ├── UndertaleEmbeddedAudio.cs │ ├── UndertaleEmbeddedImage.cs │ ├── UndertaleEmbeddedTexture.cs │ ├── UndertaleExtension.cs │ ├── UndertaleFeatureFlags.cs │ ├── UndertaleFilterEffect.cs │ ├── UndertaleFont.cs │ ├── UndertaleFunction.cs │ ├── UndertaleGameObject.cs │ ├── UndertaleGeneralInfo.cs │ ├── UndertaleGlobalInit.cs │ ├── UndertaleParticleSystem.cs │ ├── UndertalePath.cs │ ├── UndertaleRoom.cs │ ├── UndertaleScript.cs │ ├── UndertaleSequence.cs │ ├── UndertaleShader.cs │ ├── UndertaleSound.cs │ ├── UndertaleSprite.cs │ ├── UndertaleString.cs │ ├── UndertaleTags.cs │ ├── UndertaleTextureGroupInfo.cs │ ├── UndertaleTexturePageItem.cs │ ├── UndertaleTimeline.cs │ ├── UndertaleUINode.cs │ └── UndertaleVariable.cs ├── ModelsDebug │ ├── UndertaleDebugInfo.cs │ ├── UndertaleInstanceVars.cs │ └── UndertaleScriptSource.cs ├── Scripting │ └── IScriptInterface.cs ├── UndertaleBaseTypes.cs ├── UndertaleChunkTypes.cs ├── UndertaleChunks.cs ├── UndertaleData.cs ├── UndertaleDataExtensionMethods.cs ├── UndertaleDebugChunks.cs ├── UndertaleDebugData.cs ├── UndertaleIO.cs ├── UndertaleLists.cs ├── UndertaleModLib.csproj ├── UndertaleSerializationException.cs └── Util │ ├── AdaptiveBinaryReader.cs │ ├── AssetReferenceTypes.cs │ ├── BufferBinaryReader.cs │ ├── BufferBinaryWriter.cs │ ├── CombinedPrimitives.cs │ ├── DebugUtil.cs │ ├── FileBinaryReader.cs │ ├── FileBinaryWriter.cs │ ├── GMImage.cs │ ├── GitVersion.cs │ ├── MurmurHash.cs │ ├── QoiConverter.cs │ ├── StringBuilderHelper.cs │ └── TextureWorker.cs ├── UndertaleModLibTests ├── LocalGameListTest.cs ├── Models │ ├── EmbeddedAudioTest.cs │ └── StringTest.cs ├── UndertaleModLibTests.csproj └── Util │ └── MurmurHashTest.cs ├── UndertaleModTests ├── GameLoadingTests.cs ├── GamePaths.cs ├── GameScriptTests.cs └── UndertaleModTests.csproj ├── UndertaleModTool.sln ├── UndertaleModTool ├── App.xaml ├── App.xaml.cs ├── Controls │ ├── AudioFileReference.xaml │ ├── AudioFileReference.xaml.cs │ ├── ColorPicker.xaml │ ├── ColorPicker.xaml.cs │ ├── DataUserControl.cs │ ├── FlagsBox.xaml │ ├── FlagsBox.xaml.cs │ ├── ResourceListTreeViewItem.xaml │ ├── ResourceListTreeViewItem.xaml.cs │ ├── System │ │ ├── ButtonDark.cs │ │ ├── ComboBoxDark.cs │ │ ├── ContextMenuDark.cs │ │ ├── DataGridDark.cs │ │ ├── MenuItemDark.cs │ │ ├── RepeatButtonDark.cs │ │ ├── TabControlDark.cs │ │ └── TextBoxDark.cs │ ├── TileLayerImage.cs │ ├── TransparencyGridBrush.xaml │ ├── UndertaleObjectReference.xaml │ ├── UndertaleObjectReference.xaml.cs │ ├── UndertaleRoomRenderer.xaml │ ├── UndertaleRoomRenderer.xaml.cs │ ├── UndertaleStringReference.xaml │ ├── UndertaleStringReference.xaml.cs │ ├── UndertaleTexturePageItemDisplay.xaml │ └── UndertaleTexturePageItemDisplay.xaml.cs ├── Converters │ ├── BooleanToVisibilityConverter.cs │ ├── ByteArrayConverter.cs │ ├── ByteGUIDArrayConverter.cs │ ├── CompareNumbersConverter.cs │ ├── DataFieldOneTimeConverter.cs │ ├── EqualityConverter.cs │ ├── EventNameConverter.cs │ ├── FilteredViewConverter.cs │ ├── ForwardButtonEnabledConverter.cs │ ├── GameObjectByIdConverter.cs │ ├── GameObjectToStringConverter.cs │ ├── GridConverter.cs │ ├── ImplementsInterfaceConverter.cs │ ├── InvertBooleanConverter.cs │ ├── IsNullConverter.cs │ ├── IsVersionAtLeastConverter.cs │ ├── MaskImageConverter.cs │ ├── NegateNumberConverter.cs │ ├── NullToVisibilityConverter.cs │ ├── RectConverter.cs │ ├── ResourceTreeFilteredViewConverter.cs │ ├── StringTitleConverter.cs │ ├── SumRectConverter.cs │ └── UndertaleCachedImageLoader.cs ├── Corrections │ └── README.txt ├── Editors │ ├── UndertaleAudioGroupEditor.xaml │ ├── UndertaleAudioGroupEditor.xaml.cs │ ├── UndertaleBackgroundEditor.xaml │ ├── UndertaleBackgroundEditor.xaml.cs │ ├── UndertaleCodeEditor.xaml │ ├── UndertaleCodeEditor.xaml.cs │ ├── UndertaleCodeLocalsEditor.xaml │ ├── UndertaleCodeLocalsEditor.xaml.cs │ ├── UndertaleEmbeddedAudioEditor.xaml │ ├── UndertaleEmbeddedAudioEditor.xaml.cs │ ├── UndertaleEmbeddedImageEditor.xaml │ ├── UndertaleEmbeddedImageEditor.xaml.cs │ ├── UndertaleEmbeddedTextureEditor.xaml │ ├── UndertaleEmbeddedTextureEditor.xaml.cs │ ├── UndertaleExtensionEditor.xaml │ ├── UndertaleExtensionEditor.xaml.cs │ ├── UndertaleExtensionFileEditor.xaml │ ├── UndertaleExtensionFileEditor.xaml.cs │ ├── UndertaleExtensionFunctionEditor.xaml │ ├── UndertaleExtensionFunctionEditor.xaml.cs │ ├── UndertaleFontEditor │ │ ├── EditGlyphRectangleWindow.xaml │ │ ├── EditGlyphRectangleWindow.xaml.cs │ │ ├── UndertaleFontEditor.xaml │ │ └── UndertaleFontEditor.xaml.cs │ ├── UndertaleFunctionEditor.xaml │ ├── UndertaleFunctionEditor.xaml.cs │ ├── UndertaleGameEndEditor.xaml │ ├── UndertaleGameEndEditor.xaml.cs │ ├── UndertaleGameObjectEditor.xaml │ ├── UndertaleGameObjectEditor.xaml.cs │ ├── UndertaleGeneralInfoEditor.xaml │ ├── UndertaleGeneralInfoEditor.xaml.cs │ ├── UndertaleGlobalInitEditor.xaml │ ├── UndertaleGlobalInitEditor.xaml.cs │ ├── UndertaleParticleSystemEditor.xaml │ ├── UndertaleParticleSystemEditor.xaml.cs │ ├── UndertaleParticleSystemEmitterEditor.xaml │ ├── UndertaleParticleSystemEmitterEditor.xaml.cs │ ├── UndertalePathEditor.xaml │ ├── UndertalePathEditor.xaml.cs │ ├── UndertaleRoomEditor │ │ ├── UndertaleRoomEditor.xaml │ │ ├── UndertaleRoomEditor.xaml.cs │ │ ├── UndertaleTileEditor.xaml │ │ └── UndertaleTileEditor.xaml.cs │ ├── UndertaleScriptEditor.xaml │ ├── UndertaleScriptEditor.xaml.cs │ ├── UndertaleShaderEditor.xaml │ ├── UndertaleShaderEditor.xaml.cs │ ├── UndertaleSoundEditor.xaml │ ├── UndertaleSoundEditor.xaml.cs │ ├── UndertaleSpriteEditor.xaml │ ├── UndertaleSpriteEditor.xaml.cs │ ├── UndertaleStringEditor.xaml │ ├── UndertaleStringEditor.xaml.cs │ ├── UndertaleTextureGroupInfoEditor.xaml │ ├── UndertaleTextureGroupInfoEditor.xaml.cs │ ├── UndertaleTexturePageItemEditor.xaml │ ├── UndertaleTexturePageItemEditor.xaml.cs │ ├── UndertaleTimelineEditor.xaml │ ├── UndertaleTimelineEditor.xaml.cs │ ├── UndertaleVariableChunkEditor.xaml │ ├── UndertaleVariableChunkEditor.xaml.cs │ ├── UndertaleVariableEditor.xaml │ └── UndertaleVariableEditor.xaml.cs ├── FileAssociations.cs ├── FodyWeavers.xml ├── FodyWeavers.xsd ├── MainWindow.xaml ├── MainWindow.xaml.cs ├── NullConditionalDataTemplateSelector.cs ├── ProfileSystem.cs ├── Program.cs ├── Properties │ ├── AssemblyInfo.cs │ ├── Resources.Designer.cs │ ├── Resources.resx │ ├── Settings.Designer.cs │ └── Settings.settings ├── Resources │ ├── GML.xshd │ ├── VMASM.xshd │ ├── X.png │ ├── X_Down.png │ ├── arrow_blue.png │ ├── arrow_red.png │ ├── tabs_left_button.png │ └── tabs_right_button.png ├── ScriptingFunctions.cs ├── Scripts │ ├── Resource Exporters │ │ ├── ExportAllAssembly.csx │ │ ├── ExportAllCode.csx │ │ ├── ExportAllEmbeddedTextures.csx │ │ ├── ExportAllFonts.csx │ │ ├── ExportAllMasks.csx │ │ ├── ExportAllRoomsToPNG.csx │ │ ├── ExportAllShaders.csx │ │ ├── ExportAllSounds.csx │ │ ├── ExportAllSprites.csx │ │ ├── ExportAllStrings.csx │ │ ├── ExportAllStringsJSON.csx │ │ ├── ExportAllTextures.csx │ │ ├── ExportAllTexturesGrouped.csx │ │ ├── ExportAllTilesets.csx │ │ ├── ExportSpecificCode.csx │ │ ├── ExportSpecificSprites.csx │ │ ├── ExportSpritesAsGIF.csx │ │ └── ExportTextureGroups.csx │ ├── Resource Importers │ │ ├── ApplyBasicGraphicsMod.csx │ │ ├── ImportAllEmbeddedTextures.csx │ │ ├── ImportAllStrings.csx │ │ ├── ImportAllStringsJSON.csx │ │ ├── ImportAllTilesets.csx │ │ ├── ImportAssembly.csx │ │ ├── ImportFonts.csx │ │ ├── ImportGML.csx │ │ ├── ImportGMS2FontData.csx │ │ ├── ImportGraphics.csx │ │ ├── ImportGraphicsAdvanced.csx │ │ ├── ImportMasks.csx │ │ ├── ImportShaders.csx │ │ ├── ImportSingleSound.csx │ │ ├── ImportSounds.csx │ │ ├── NewTextureRepacker.csx │ │ └── ReduceEmbeddedTexturePages.csx │ ├── Sample Scripts │ │ ├── DeltaHATE.csx │ │ ├── DeltaMILK.csx │ │ ├── HeCanBeEverywhere.csx │ │ ├── MixMod.csx │ │ ├── MixMod_README.md │ │ ├── RoomOfDetermination.csx │ │ └── TheWholeWorldRevolving.csx │ ├── Technical Scripts │ │ ├── 13_To_14.csx │ │ ├── 14_To_16.csx │ │ ├── 15_To_16.csx │ │ ├── 15_to_17_To_16.csx │ │ ├── 16_To_17.csx │ │ ├── AutoBackup.csx │ │ ├── CheckDecompiler.csx │ │ ├── ConvertFrom17to16.csx │ │ ├── ConvertFrom17to16_for_2.3.csx │ │ ├── CopySound.csx │ │ ├── CopySoundInternal.csx │ │ ├── CopySpriteBgFont.csx │ │ ├── CopySpriteBgFontInternal.csx │ │ ├── ExecutionOrder.csx │ │ ├── ExportAllCodeSync.csx │ │ ├── ExportAssetOrder.csx │ │ ├── FindNullsInOffsetMap.csx │ │ ├── FindUnknownFunctions.csx │ │ ├── FindUnusedStrings.csx │ │ ├── GameObjectCopy.csx │ │ ├── GameObjectCopyInternal.csx │ │ ├── GetAllChunkNames.csx │ │ ├── GetTempDirectory.csx │ │ ├── ImportAssetOrder.csx │ │ ├── ImportGraphics_Full_Repack.csx │ │ ├── LintAllScripts.csx │ │ ├── MatchRoomOrderToInternalOrder.csx │ │ ├── Profiler.csx │ │ ├── RealignRoomInternalOrder.csx │ │ ├── RemoveUnusedSounds.csx │ │ ├── RestoreMissingCodeLocals.csx │ │ ├── SpriteOriginCopy.csx │ │ ├── TestExportAllCode.csx │ │ ├── ToggleAlignment.csx │ │ └── VariableFixer.csx │ ├── UTDR Scripts │ │ ├── Borders │ │ │ ├── bg_border_anime.png │ │ │ ├── bg_border_casino.png │ │ │ ├── bg_border_castle.png │ │ │ ├── bg_border_dog.png │ │ │ ├── bg_border_fire.png │ │ │ ├── bg_border_line.png │ │ │ ├── bg_border_line_1080.png │ │ │ ├── bg_border_rad.png │ │ │ ├── bg_border_ruins.png │ │ │ ├── bg_border_sepia.png │ │ │ ├── bg_border_truelab.png │ │ │ ├── bg_border_tundra.png │ │ │ ├── bg_border_water1.png │ │ │ ├── border_dark.png │ │ │ ├── border_dark_ch1_0.png │ │ │ ├── border_dw_castletown_0.png │ │ │ ├── border_dw_city_0.png │ │ │ ├── border_dw_cyber_0.png │ │ │ ├── border_dw_mansion_0.png │ │ │ ├── border_light.png │ │ │ ├── border_light_ch1_0.png │ │ │ ├── border_line_1080_0.png │ │ │ └── border_lw_town_0.png │ │ ├── Debug.csx │ │ ├── DeltaruneClearShaderData.csx │ │ ├── DeltaruneReloadJSON.csx │ │ ├── DeltaruneTTFFonts.csx │ │ ├── DisableDogcheck.csx │ │ ├── ShowRoomName.csx │ │ ├── TouchControlsEnabler.csx │ │ ├── TouchControls_data │ │ │ ├── controls.png │ │ │ ├── gml_Object_obj_mobilecontrols_Create_0.gml │ │ │ ├── gml_Object_obj_mobilecontrols_Draw_64.gml │ │ │ ├── gml_Object_obj_mobilecontrols_Other_4.gml │ │ │ └── gml_Object_obj_mobilecontrols_Step_0.gml │ │ ├── UndertaleBattlegroupSelector.csx │ │ ├── UndertaleBetterVaporiser.csx │ │ ├── UndertaleBorderEnabler.csx │ │ ├── UndertaleBorderEnablerv1_11.csx │ │ ├── UndertaleChangeHomeBattlegroup.csx │ │ ├── UndertaleDebugMsg.csx │ │ ├── UndertaleDebugToggler.csx │ │ ├── UndertaleDialogSimulator.csx │ │ ├── UndertaleFixAlphysLabCrashAndroid.csx │ │ ├── UndertaleGoToRoom.csx │ │ ├── UndertaleRunButton.csx │ │ ├── UndertaleSafeBlaster.csx │ │ ├── UndertaleSimplifyBattlegroupScript.csx │ │ ├── UndertaleSwitchAndXboxOnPC.csx │ │ ├── UndertaleTTFFonts.csx │ │ ├── UndertaleWASD.csx │ │ └── UndertaleWithJSONs.csx │ └── Utility Scripts │ │ ├── ExternalizeAllOGGs.csx │ │ ├── ExtractEmbeddedDataFile.csx │ │ ├── FancyRoomSelect.csx │ │ ├── FindAndReplace.csx │ │ ├── FindObjectsInRooms.csx │ │ ├── FindObjectsWithSprite.csx │ │ ├── FontEditor.csx │ │ ├── FontEditor_README.md │ │ ├── GoToRoom_AutoLocatePersistentObj.csx │ │ ├── MergeImages.csx │ │ └── ScaleAllTextures.csx ├── Settings.cs ├── Tab.cs ├── UndertaleModTool.csproj ├── WindowExtensions.cs ├── Windows │ ├── ClickableTextOutput.xaml │ ├── ClickableTextOutput.xaml.cs │ ├── DebugDataDialog.xaml │ ├── DebugDataDialog.xaml.cs │ ├── FindReferencesTypesDialog │ │ ├── FindReferencesResults.xaml │ │ ├── FindReferencesResults.xaml.cs │ │ ├── FindReferencesTypesDialog.xaml │ │ ├── FindReferencesTypesDialog.xaml.cs │ │ ├── UndertaleResourceReferenceMap.cs │ │ └── UndertaleResourceReferenceMethodsMap.cs │ ├── GMLSettingsWindow.xaml │ ├── GMLSettingsWindow.xaml.cs │ ├── LoaderDialog.xaml │ ├── LoaderDialog.xaml.cs │ ├── RuntimePicker.xaml │ ├── RuntimePicker.xaml.cs │ ├── SearchInCodeWindow.xaml │ ├── SearchInCodeWindow.xaml.cs │ ├── SettingsWindow.xaml │ ├── SettingsWindow.xaml.cs │ ├── StringUpdateWindow.xaml │ ├── StringUpdateWindow.xaml.cs │ ├── TextInput.Designer.cs │ ├── TextInput.cs │ ├── TextInput.resx │ ├── TextInputDialog.xaml │ └── TextInputDialog.xaml.cs ├── app.manifest └── icon.ico ├── UndertaleModToolUpdater ├── Program.cs └── UndertaleModToolUpdater.csproj └── images ├── flowey.gif ├── logo.png └── ribbit-dr.png /.gitattributes: -------------------------------------------------------------------------------- 1 | UndertaleModTool/Corrections/** linguist-generated=true -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.yml: -------------------------------------------------------------------------------- 1 | name: Bug Report 2 | description: Something not behaving the way it should? Let us know! 3 | labels: ["bug"] 4 | body: 5 | - type: textarea 6 | attributes: 7 | label: Describe the bug 8 | description: A clear description of what the bug is. Please consider adding pictures and/or videos, as they really help! 9 | placeholder: | 10 | When doing X, UndertaleModTool crashes. 11 | validations: 12 | required: true 13 | - type: textarea 14 | attributes: 15 | label: Reproducing steps 16 | description: Steps to reproduce the behavior. 17 | placeholder: | 18 | 1. Open X ... 19 | 2. Do Y ... 20 | 3. See error 21 | validations: 22 | required: true 23 | - type: textarea 24 | attributes: 25 | label: Setup Details 26 | description: A clear and concise description of your tools and enviroment used. 27 | placeholder: | 28 | 1. UndertaleModTool version: What UndertaleModTool version did you use? If compiled from source, please specify the commit number. 29 | 2. Operating system: Specify the OS and version e.g. Windows 10, Mac OS X Catalina, Ubuntu 20.04, etc. 30 | 3. GameMaker Game: What GameMaker game was used? Link to it! 31 | validations: 32 | required: true -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: UnderMiners Discord 4 | url: https://discord.gg/hnyMDypMbN 5 | about: For UndertaleModTool questions, discussion, analysis and feedback -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.yml: -------------------------------------------------------------------------------- 1 | name: Suggest a Feature 2 | description: Have an idea for something completely new that UndertaleModTool could benefit from having? Or for improving something that already exists? Let us know! 3 | labels: ["enhancement"] 4 | body: 5 | - type: dropdown 6 | attributes: 7 | label: Which component should be improved? 8 | options: [UI/UX, Decompiler, Compiler, CLI, Lib, Other] 9 | validations: 10 | required: true 11 | - type: textarea 12 | attributes: 13 | label: Describe your feature suggestion in more detail 14 | description: A clear description of your feature suggestion. Feel free to add pictures or videos directly to this description! 15 | placeholder: | 16 | Give a clear explanation of what you want us to add. 17 | Please don't give vague suggestions like "Make button look better" or "Add a new panel". 18 | How exactly does it work? What are the inputs? What is the expected outcome? 19 | validations: 20 | required: true -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Description 2 | 3 | 4 | ### Caveats 5 | 6 | 7 | ### Notes 8 | -------------------------------------------------------------------------------- /.github/dependabot.yaml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "nuget" # See documentation for possible values 9 | directory: "/" # Location of package manifests 10 | schedule: 11 | interval: "daily" 12 | labels: 13 | - "dependencies" 14 | 15 | - package-ecosystem: "github-actions" 16 | directory: "/" 17 | schedule: 18 | interval: "daily" 19 | labels: 20 | - "dependencies" 21 | -------------------------------------------------------------------------------- /.github/workflows/publish_cli.yml: -------------------------------------------------------------------------------- 1 | name: Publish CLI 2 | 3 | on: 4 | #push: 5 | # branches: [ master ] 6 | workflow_dispatch: 7 | 8 | jobs: 9 | publish_cli: 10 | 11 | strategy: 12 | fail-fast: false 13 | matrix: 14 | os: [ubuntu-latest, macOS-latest, windows-latest] 15 | configuration: [Debug] 16 | bundled: [true, false] 17 | include: 18 | - os: ubuntu-latest 19 | rid: linux-x64 20 | - os: macOS-latest 21 | rid: osx-x64 22 | - os: windows-latest 23 | rid: win-x64 24 | 25 | runs-on: ${{ matrix.os }} 26 | 27 | steps: 28 | - uses: actions/checkout@v4 29 | with: 30 | submodules: true 31 | - name: Setup .NET 32 | uses: actions/setup-dotnet@v4 33 | with: 34 | dotnet-version: 8.0.x 35 | - name: Restore dependencies 36 | run: dotnet restore UndertaleModCli 37 | - name: Build 38 | run: dotnet build UndertaleModCli --no-restore 39 | - name: Publish ${{ matrix.os }} CLI 40 | run: dotnet publish UndertaleModCli -c ${{ matrix.configuration }} -r ${{ matrix.rid }} --self-contained ${{ matrix.bundled }} -p:PublishSingleFile=false --output CLI-${{ matrix.os }} 41 | - name: Copy external files 42 | run: | 43 | cp ./README.md ./CLI-${{ matrix.os }}/ 44 | cp ./SCRIPTS.md ./CLI-${{ matrix.os }}/ 45 | cp ./LICENSE.txt ./CLI-${{ matrix.os }}/ 46 | cp -r ./UndertaleModLib/GameSpecificData/ ./CLI-${{ matrix.os }}/GameSpecificData/ 47 | cp -r ./UndertaleModTool/Scripts/ ./CLI-${{ matrix.os }}/Scripts/ 48 | - name: Upload ${{ matrix.os }} CLI 49 | uses: actions/upload-artifact@v4 50 | with: 51 | name: CLI-${{ matrix.os }}-isBundled-${{ matrix.bundled }} 52 | path: CLI-${{ matrix.os }} 53 | -------------------------------------------------------------------------------- /.github/workflows/publish_gui.yml: -------------------------------------------------------------------------------- 1 | name: Publish GUI 2 | 3 | on: 4 | #push: 5 | # branches: [ master ] 6 | workflow_dispatch: 7 | 8 | jobs: 9 | publish_gui: 10 | 11 | strategy: 12 | fail-fast: false 13 | matrix: 14 | os: [windows-latest] 15 | configuration: [Debug] 16 | bundled: [true, false] 17 | singlefile: [true, false] 18 | 19 | runs-on: ${{ matrix.os }} 20 | 21 | steps: 22 | - uses: actions/checkout@v4 23 | with: 24 | submodules: true 25 | - name: Setup .NET 26 | uses: actions/setup-dotnet@v4 27 | with: 28 | dotnet-version: 8.0.x 29 | - name: Restore dependencies 30 | run: dotnet restore 31 | - name: Build 32 | run: | 33 | dotnet build UndertaleModTool --no-restore 34 | dotnet build UndertaleModToolUpdater --no-restore 35 | - name: Publish ${{ matrix.os }} GUI 36 | run: | 37 | dotnet publish UndertaleModTool -c ${{ matrix.configuration }} -r win-x64 --self-contained ${{ matrix.bundled }} -p:PublishSingleFile=${{ matrix.singlefile }} --output ${{ matrix.os }} 38 | dotnet publish UndertaleModToolUpdater -c ${{ matrix.configuration }} -r win-x64 --self-contained ${{ matrix.bundled }} -p:PublishSingleFile=false --output ${{ matrix.os }}/Updater 39 | - name: Copy external files 40 | run: | 41 | cp ./README.md ./${{ matrix.os }} 42 | cp ./SCRIPTS.md ./${{ matrix.os }} 43 | cp ./LICENSE.txt ./${{ matrix.os }} 44 | cp -r ./UndertaleModLib/GameSpecificData/ ./${{ matrix.os }}/GameSpecificData/ 45 | - name: Upload ${{ matrix.os }} GUI 46 | uses: actions/upload-artifact@v4 47 | with: 48 | name: GUI-${{ matrix.os }}-isBundled-${{ matrix.bundled }}-isSingleFile-${{ matrix.singlefile }} 49 | path: ${{ matrix.os }} 50 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "Underanalyzer"] 2 | path = Underanalyzer 3 | url = https://github.com/UnderminersTeam/Underanalyzer.git 4 | -------------------------------------------------------------------------------- /UndertaleModCli/CommandOptions/DumpOptions.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | 3 | namespace UndertaleModCli; 4 | 5 | /// 6 | /// Cli options for the Dump command 7 | /// 8 | public class DumpOptions 9 | { 10 | /// 11 | /// File path to the data file 12 | /// 13 | public FileInfo Datafile { get; set; } 14 | 15 | /// 16 | /// Directory path to where to dump all contents 17 | /// 18 | public DirectoryInfo Output { get; set; } 19 | 20 | /// 21 | /// Determines if Cli should print out verbose logs 22 | /// 23 | public bool Verbose { get; set; } = false; 24 | 25 | /// 26 | /// Names of the code entries that should get dumped 27 | /// 28 | public string[] Code { get; set; } 29 | 30 | /// 31 | /// Determines if strings should get dumped. 32 | /// 33 | public bool Strings { get; set; } 34 | 35 | /// 36 | /// Determines if embedded textures should get dumped 37 | /// 38 | public bool Textures { get; set; } 39 | } 40 | -------------------------------------------------------------------------------- /UndertaleModCli/CommandOptions/InfoOptions.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | 3 | namespace UndertaleModCli; 4 | 5 | /// 6 | /// Cli options for the Info command 7 | /// 8 | public class InfoOptions 9 | { 10 | /// 11 | /// File path to the data file 12 | /// 13 | public FileInfo Datafile { get; set; } 14 | 15 | /// 16 | /// Determines if Cli should print out verbose logs 17 | /// 18 | public bool Verbose { get; set; } = false; 19 | } 20 | -------------------------------------------------------------------------------- /UndertaleModCli/CommandOptions/LoadOptions.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | 3 | namespace UndertaleModCli; 4 | 5 | /// 6 | /// Cli options for the Load command 7 | /// 8 | public class LoadOptions 9 | { 10 | /// 11 | /// File path to the data file 12 | /// 13 | public FileInfo Datafile { get; set; } 14 | 15 | /// 16 | /// File paths to the scripts that shall be run 17 | /// 18 | public FileInfo[] Scripts { get; set; } 19 | 20 | /// 21 | /// C# string that shall be executed 22 | /// 23 | public string Line { get; set; } 24 | 25 | /// 26 | /// File path to where to save the modified data file 27 | /// 28 | public FileInfo Output { get; set; } 29 | 30 | /// 31 | /// Determines if Cli should be run in interactive mode 32 | /// 33 | public bool Interactive { get; set; } = false; 34 | 35 | /// 36 | /// Determines if Cli should print out verbose logs 37 | /// 38 | public bool Verbose { get; set; } = false; 39 | } 40 | -------------------------------------------------------------------------------- /UndertaleModCli/CommandOptions/NewOptions.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | 3 | namespace UndertaleModCli; 4 | 5 | /// 6 | /// Cli options for the New command 7 | /// 8 | public class NewOptions 9 | { 10 | /// 11 | /// File path for new data file 12 | /// 13 | public FileInfo Output { get; set; } = new FileInfo("data.win"); 14 | 15 | /// 16 | /// If the existing file path at should be overwritten 17 | /// 18 | public bool Overwrite { get; set; } = false; 19 | 20 | /// 21 | /// Whether to write the new data to Stdout 22 | /// 23 | public bool Stdout { get; set; } 24 | 25 | /// 26 | /// Determines if Cli should print out verbose logs 27 | /// 28 | public bool Verbose { get; set; } = false; 29 | } 30 | -------------------------------------------------------------------------------- /UndertaleModCli/CommandOptions/ReplaceOptions.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | 3 | namespace UndertaleModCli; 4 | 5 | /// 6 | /// Cli options for the Replace command 7 | /// 8 | public class ReplaceOptions 9 | { 10 | /// 11 | /// File path to the data file 12 | /// 13 | public FileInfo Datafile { get; set; } 14 | 15 | /// 16 | /// File path to where to save the modified data file 17 | /// 18 | public FileInfo Output { get; set; } 19 | 20 | /// 21 | /// Determines if Cli should print out verbose logs 22 | /// 23 | public bool Verbose { get; set; } = false; 24 | 25 | /// 26 | /// Equal separated values of code entry and the file to replace the code entry with. 27 | /// 28 | public string[] Code { get; set; } 29 | 30 | /// 31 | /// Equal separated values of embedded texture and the file to replace the embedded texture with. 32 | /// 33 | public string[] Textures { get; set; } 34 | } 35 | -------------------------------------------------------------------------------- /UndertaleModCli/UndertaleModCli.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | AnyCPU;x64 7 | disable 8 | LatestMajor 9 | True 10 | en 11 | en 12 | en 13 | 14 | 1591 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | PreserveNewest 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /UndertaleModCli/runtimeconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "configProperties": { 3 | "System.Drawing.EnableUnixSupport": true 4 | } 5 | } -------------------------------------------------------------------------------- /UndertaleModLib/FodyWeavers.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | -------------------------------------------------------------------------------- /UndertaleModLib/GameSpecificData/Definitions/deltarune.json: -------------------------------------------------------------------------------- 1 | { 2 | "LoadOrder": 1, 3 | "Conditions": [ 4 | { 5 | "ConditionKind": "DisplayName.Regex", 6 | "Value": "(?i)^SURVEY_PROGRAM$" 7 | }, 8 | { 9 | "ConditionKind": "DisplayName.Regex", 10 | "Value": "(?i)^deltarune" 11 | } 12 | ], 13 | "UnderanalyzerFilename": "deltarune.json" 14 | } -------------------------------------------------------------------------------- /UndertaleModLib/GameSpecificData/Definitions/gamemaker.json: -------------------------------------------------------------------------------- 1 | { 2 | "LoadOrder": 0, 3 | "Conditions": [ 4 | { 5 | "ConditionKind": "Always" 6 | } 7 | ], 8 | "UnderanalyzerFilename": "gamemaker.json" 9 | } -------------------------------------------------------------------------------- /UndertaleModLib/GameSpecificData/Definitions/undertale.json: -------------------------------------------------------------------------------- 1 | { 2 | "LoadOrder": 1, 3 | "Conditions": [ 4 | { 5 | "ConditionKind": "DisplayName.Regex", 6 | "Value": "(?i)^undertale" 7 | } 8 | ], 9 | "UnderanalyzerFilename": "undertale.json" 10 | } -------------------------------------------------------------------------------- /UndertaleModLib/GameSpecificData/README.txt: -------------------------------------------------------------------------------- 1 | This folder contains data for specific games, to improve the modding experience when using certain features. 2 | 3 | Games are defined in the "Definitions" sub-folder, where they are given conditions to match the game(s) they target. Any JSON files from this folder are loaded automatically, if available. 4 | 5 | GML decompiler configs are contained within the "Underanalyzer" sub-folder, referenced from the original definition files. (These are loaded on-demand.) 6 | -------------------------------------------------------------------------------- /UndertaleModLib/GameSpecificData/Underanalyzer/template.json: -------------------------------------------------------------------------------- 1 | { 2 | "Types": { 3 | "Enums": { 4 | }, 5 | "Constants": { 6 | }, 7 | "General": { 8 | } 9 | }, 10 | "GlobalNames": { 11 | "Variables": { 12 | }, 13 | "FunctionArguments": { 14 | }, 15 | "FunctionReturn": { 16 | } 17 | }, 18 | "CodeEntryNames": { 19 | } 20 | } -------------------------------------------------------------------------------- /UndertaleModLib/Models/UndertaleAudioGroup.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace UndertaleModLib.Models; 4 | 5 | /// 6 | /// Audio group entry in a data file. 7 | /// 8 | /// Audio Groups allow you to manage a set sound entries easier. 9 | /// You can use these for memory management, volume control and more.
10 | /// Audio Groups are only available to use in the regular audio system
11 | /// 12 | /// 13 | [PropertyChanged.AddINotifyPropertyChangedInterface] 14 | public class UndertaleAudioGroup : UndertaleNamedResource, IStaticChildObjectsSize, IDisposable 15 | { 16 | /// 17 | public static readonly uint ChildObjectsSize = 4; 18 | 19 | /// 20 | /// The name of the audio group. 21 | /// 22 | /// This is how the audio group is referenced from code. 23 | public UndertaleString Name { get; set; } 24 | 25 | /// 26 | /// Relative path (from the main data file) to the audio group file, in GameMaker 2024.14 and above. 27 | /// 28 | /// 29 | /// Prior to 2024.14, audio groups were all numerically assigned filenames and all in the root directory. 30 | /// 31 | public UndertaleString Path { get; set; } = null; 32 | 33 | /// 34 | public void Serialize(UndertaleWriter writer) 35 | { 36 | writer.WriteUndertaleString(Name); 37 | if (writer.undertaleData.IsVersionAtLeast(2024, 14)) 38 | { 39 | writer.WriteUndertaleString(Path); 40 | } 41 | } 42 | 43 | /// 44 | public void Unserialize(UndertaleReader reader) 45 | { 46 | Name = reader.ReadUndertaleString(); 47 | if (reader.undertaleData.IsVersionAtLeast(2024, 14)) 48 | { 49 | Path = reader.ReadUndertaleString(); 50 | } 51 | } 52 | 53 | /// 54 | public override string ToString() 55 | { 56 | return $"{Name?.Content} ({GetType().Name})"; 57 | } 58 | 59 | /// 60 | public void Dispose() 61 | { 62 | GC.SuppressFinalize(this); 63 | 64 | Name = null; 65 | Path = null; 66 | } 67 | } -------------------------------------------------------------------------------- /UndertaleModLib/Models/UndertaleEmbeddedAudio.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | 4 | namespace UndertaleModLib.Models; 5 | 6 | /// 7 | /// An embedded audio entry in a data file. 8 | /// 9 | [PropertyChanged.AddINotifyPropertyChangedInterface] 10 | public class UndertaleEmbeddedAudio : UndertaleNamedResource, PaddedObject, IDisposable 11 | { 12 | /// 13 | /// The name of the embedded audio entry. 14 | /// 15 | /// This is a UTMT only attribute. GameMaker does not store names for them. 16 | public UndertaleString Name { get; set; } 17 | 18 | /// 19 | /// The audio data of the embedded audio entry. 20 | /// 21 | public byte[] Data { get; set; } = Array.Empty(); 22 | 23 | /// 24 | public void Serialize(UndertaleWriter writer) 25 | { 26 | writer.Write((uint)Data.Length); 27 | writer.Write(Data); 28 | } 29 | 30 | /// 31 | public void SerializePadding(UndertaleWriter writer) 32 | { 33 | while (writer.Position % 4 != 0) 34 | writer.Write((byte)0); 35 | } 36 | 37 | /// 38 | public void Unserialize(UndertaleReader reader) 39 | { 40 | uint len = reader.ReadUInt32(); 41 | Data = reader.ReadBytes((int)len); 42 | Util.DebugUtil.Assert(Data.Length == len); 43 | } 44 | 45 | /// 46 | public void UnserializePadding(UndertaleReader reader) 47 | { 48 | while (reader.AbsPosition % 4 != 0) 49 | if (reader.ReadByte() != 0) 50 | throw new IOException("Padding error!"); 51 | } 52 | 53 | /// 54 | public override string ToString() 55 | { 56 | try 57 | { 58 | // TODO: Does only the GUI set this? 59 | return $"{Name.Content} ({GetType().Name})"; 60 | } 61 | catch 62 | { 63 | Name = new UndertaleString("EmbeddedSound Unknown Index"); 64 | } 65 | return $"{Name.Content} ({GetType().Name})"; 66 | } 67 | 68 | /// 69 | public void Dispose() 70 | { 71 | GC.SuppressFinalize(this); 72 | 73 | Name = null; 74 | Data = Array.Empty(); 75 | } 76 | } -------------------------------------------------------------------------------- /UndertaleModLib/Models/UndertaleEmbeddedImage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace UndertaleModLib.Models; 4 | 5 | /// 6 | /// An embedded image entry in a GameMaker data file. This is GMS2 only.
7 | /// Not to be confused with the other "embedded" resources, this is a bit different. 8 | ///
9 | /// Rough structure: 10 | /// EMBI - stands for "embedded images"? 11 | /// Couldn't figure out how to fill the chunk with any data by editing a project and recompiling... 12 | /// --- 13 | /// <normal FourCC and chunk length found in every chunk> 14 | /// 15 | /// Int32 - literally just the number 1, always (unknown) 16 | /// 17 | /// Array of some kind: 18 | /// Int32 - count of items in array 19 | /// 20 | /// Each item: 21 | /// 32-bit string pointer, some kind of texture page identifier? 22 | /// 32-bit pointer to something relating to a texture page entry? 23 | /// 24 | /// . 25 | public class UndertaleEmbeddedImage : UndertaleNamedResource, IStaticChildObjectsSize, IDisposable 26 | { 27 | /// 28 | public static readonly uint ChildObjectsSize = 8; 29 | 30 | /// 31 | /// The name of the . 32 | /// 33 | public UndertaleString Name { get; set; } 34 | 35 | /// 36 | /// The of this . 37 | /// 38 | public UndertaleTexturePageItem TextureEntry { get; set; } 39 | 40 | /// 41 | public void Serialize(UndertaleWriter writer) 42 | { 43 | writer.WriteUndertaleString(Name); 44 | writer.WriteUndertaleObjectPointer(TextureEntry); 45 | } 46 | 47 | /// 48 | public void Unserialize(UndertaleReader reader) 49 | { 50 | Name = reader.ReadUndertaleString(); 51 | TextureEntry = reader.ReadUndertaleObjectPointer(); 52 | } 53 | 54 | /// 55 | public override string ToString() 56 | { 57 | return Name?.Content + " (" + GetType().Name + ")"; 58 | } 59 | 60 | /// 61 | public void Dispose() 62 | { 63 | GC.SuppressFinalize(this); 64 | 65 | Name = null; 66 | TextureEntry = null; 67 | } 68 | } -------------------------------------------------------------------------------- /UndertaleModLib/Models/UndertaleFeatureFlags.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace UndertaleModLib.Models; 4 | 5 | /// 6 | /// List of feature flag entries in a GameMaker data file, version 2022.8 and above. 7 | /// 8 | [PropertyChanged.AddINotifyPropertyChangedInterface] 9 | public class UndertaleFeatureFlags : UndertaleObject, IDisposable 10 | { 11 | /// 12 | /// The list of feature flags. 13 | /// 14 | public UndertaleSimpleListString List { get; set; } 15 | 16 | 17 | /// 18 | public void Serialize(UndertaleWriter writer) 19 | { 20 | List.Serialize(writer); 21 | } 22 | 23 | /// 24 | public void Unserialize(UndertaleReader reader) 25 | { 26 | List = new UndertaleSimpleListString(); 27 | List.Unserialize(reader); 28 | } 29 | 30 | /// 31 | public static uint UnserializeChildObjectCount(UndertaleReader reader) 32 | { 33 | return UndertaleSimpleListString.UnserializeChildObjectCount(reader); 34 | } 35 | 36 | /// 37 | public void Dispose() 38 | { 39 | GC.SuppressFinalize(this); 40 | 41 | List = null; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /UndertaleModLib/Models/UndertaleFilterEffect.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace UndertaleModLib.Models; 4 | 5 | /// 6 | /// A filter effect as it's used in a GameMaker data file. These are GameMaker: Studio 2.3.6+ only. 7 | /// 8 | [PropertyChanged.AddINotifyPropertyChangedInterface] 9 | public class UndertaleFilterEffect : UndertaleNamedResource, IStaticChildObjectsSize, IDisposable 10 | { 11 | /// 12 | public static readonly uint ChildObjectsSize = 8; 13 | 14 | /// 15 | /// The name of the . 16 | /// 17 | public UndertaleString Name { get; set; } 18 | 19 | /// 20 | /// TODO: fill this out please 21 | /// 22 | public UndertaleString Value { get; set; } 23 | 24 | /// 25 | public void Serialize(UndertaleWriter writer) 26 | { 27 | writer.WriteUndertaleString(Name); 28 | writer.WriteUndertaleString(Value); 29 | } 30 | 31 | /// 32 | public void Unserialize(UndertaleReader reader) 33 | { 34 | Name = reader.ReadUndertaleString(); 35 | Value = reader.ReadUndertaleString(); 36 | } 37 | 38 | /// 39 | public override string ToString() 40 | { 41 | return Name?.Content + " (" + GetType().Name + ")"; 42 | } 43 | 44 | /// 45 | public void Dispose() 46 | { 47 | GC.SuppressFinalize(this); 48 | 49 | Name = null; 50 | Value = null; 51 | } 52 | } -------------------------------------------------------------------------------- /UndertaleModLib/Models/UndertaleGlobalInit.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | 4 | namespace UndertaleModLib.Models; 5 | 6 | /// 7 | /// A global initialization entry in a data file. 8 | /// 9 | /// 10 | // TODO: Never seen in GMS1.4 so uncertain if the structure was the same. 11 | public class UndertaleGlobalInit : UndertaleObject, INotifyPropertyChanged, IDisposable 12 | { 13 | private UndertaleResourceById _code = new(); 14 | 15 | /// 16 | /// The object which contains the code. 17 | /// 18 | /// This code is executed at a global scope, before the first room of the game executes. 19 | public UndertaleCode Code { get => _code.Resource; set { _code.Resource = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Code))); } } 20 | 21 | /// 22 | public event PropertyChangedEventHandler PropertyChanged; 23 | 24 | /// 25 | public void Serialize(UndertaleWriter writer) 26 | { 27 | _code.Serialize(writer); 28 | } 29 | 30 | /// 31 | public void Unserialize(UndertaleReader reader) 32 | { 33 | _code = new UndertaleResourceById(); 34 | _code.Unserialize(reader); // Cannot use ReadUndertaleObject, as that messes things up. 35 | } 36 | 37 | /// 38 | public void Dispose() 39 | { 40 | GC.SuppressFinalize(this); 41 | 42 | _code.Dispose(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /UndertaleModLib/Models/UndertaleScript.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | 4 | namespace UndertaleModLib.Models; 5 | 6 | /// 7 | /// A script entry in a data file. 8 | /// 9 | public class UndertaleScript : UndertaleNamedResource, INotifyPropertyChanged, IStaticChildObjectsSize, IDisposable 10 | { 11 | /// 12 | public static readonly uint ChildObjectsSize = 8; 13 | 14 | /// 15 | /// The name of the script entry. 16 | /// 17 | public UndertaleString Name { get; set; } 18 | private UndertaleResourceById _code = new(); 19 | 20 | /// 21 | /// The object which contains the code. 22 | /// 23 | public UndertaleCode Code { get => _code.Resource; set { _code.Resource = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Code))); } } 24 | 25 | /// 26 | /// Whether or not this script is a constructor. 27 | /// 28 | public bool IsConstructor { get; set; } 29 | 30 | /// 31 | public event PropertyChangedEventHandler PropertyChanged; 32 | 33 | /// 34 | public void Serialize(UndertaleWriter writer) 35 | { 36 | writer.WriteUndertaleString(Name); 37 | if (IsConstructor) 38 | writer.Write((uint)_code.SerializeById(writer) | 2147483648u); 39 | else 40 | writer.WriteUndertaleObject(_code); 41 | } 42 | 43 | /// 44 | public void Unserialize(UndertaleReader reader) 45 | { 46 | Name = reader.ReadUndertaleString(); 47 | int id = reader.ReadInt32(); 48 | if (id < -1) 49 | { 50 | IsConstructor = true; 51 | id = (int)((uint)id & 2147483647u); 52 | } 53 | _code.UnserializeById(reader, id); 54 | } 55 | 56 | /// 57 | public override string ToString() 58 | { 59 | return Name?.Content + " (" + GetType().Name + ")"; 60 | } 61 | 62 | /// 63 | public void Dispose() 64 | { 65 | GC.SuppressFinalize(this); 66 | 67 | _code.Dispose(); 68 | } 69 | } -------------------------------------------------------------------------------- /UndertaleModLib/ModelsDebug/UndertaleDebugInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.ObjectModel; 2 | 3 | namespace UndertaleModLib.ModelsDebug 4 | { 5 | public class UndertaleDebugInfo : ObservableCollection, UndertaleResource 6 | { 7 | public class DebugInfoPair 8 | { 9 | public uint BytecodeOffset { get; set; } 10 | public uint SourceCodeOffset { get; set; } 11 | } 12 | 13 | public void Serialize(UndertaleWriter writer) 14 | { 15 | writer.Write((uint)Count * 2); 16 | foreach(DebugInfoPair pair in this) 17 | { 18 | writer.Write(pair.BytecodeOffset); 19 | writer.Write(pair.SourceCodeOffset); 20 | } 21 | } 22 | 23 | public void Unserialize(UndertaleReader reader) 24 | { 25 | Clear(); 26 | uint count = reader.ReadUInt32(); 27 | Util.DebugUtil.Assert(count % 2 == 0); 28 | count /= 2; 29 | for (int i = 0; i < count; i++) 30 | Add(new DebugInfoPair() { BytecodeOffset = reader.ReadUInt32(), SourceCodeOffset = reader.ReadUInt32() }); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /UndertaleModLib/ModelsDebug/UndertaleInstanceVars.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace UndertaleModLib.ModelsDebug 4 | { 5 | public class UndertaleInstanceVars : UndertaleResource 6 | { 7 | public void Serialize(UndertaleWriter writer) 8 | { 9 | throw new NotImplementedException(); 10 | } 11 | 12 | public void Unserialize(UndertaleReader reader) 13 | { 14 | throw new NotImplementedException(); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /UndertaleModLib/ModelsDebug/UndertaleScriptSource.cs: -------------------------------------------------------------------------------- 1 | using UndertaleModLib.Models; 2 | 3 | namespace UndertaleModLib.ModelsDebug 4 | { 5 | // The index of this object matches index in CODE in main data file 6 | public class UndertaleScriptSource : UndertaleResource 7 | { 8 | public UndertaleString SourceCode { get; set; } 9 | 10 | public void Serialize(UndertaleWriter writer) 11 | { 12 | writer.WriteUndertaleString(SourceCode); 13 | } 14 | 15 | public void Unserialize(UndertaleReader reader) 16 | { 17 | SourceCode = reader.ReadUndertaleString(); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /UndertaleModLib/UndertaleDebugData.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using UndertaleModLib.Models; 3 | using UndertaleModLib.ModelsDebug; 4 | 5 | namespace UndertaleModLib 6 | { 7 | public class UndertaleDebugData 8 | { 9 | public UndertaleDebugFORM FORM; 10 | 11 | public IList SourceCode => FORM.SCPT.List; 12 | public IList DebugInfo => FORM.DBGI.List; 13 | public IList InstanceVars => FORM.INST.List; 14 | public IList LocalVars => FORM.LOCL.List; 15 | public IList Strings => FORM.STRG.List; 16 | 17 | public static UndertaleDebugData CreateNew() 18 | { 19 | UndertaleDebugData data = new UndertaleDebugData(); 20 | data.FORM = new UndertaleDebugFORM(); 21 | data.FORM.Chunks["SCPT"] = new UndertaleDebugChunkSCPT(); 22 | data.FORM.Chunks["DBGI"] = new UndertaleDebugChunkDBGI(); 23 | data.FORM.Chunks["INST"] = new UndertaleDebugChunkINST(); 24 | data.FORM.Chunks["LOCL"] = new UndertaleDebugChunkLOCL(); 25 | data.FORM.Chunks["STRG"] = new UndertaleDebugChunkSTRG(); 26 | return data; 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /UndertaleModLib/UndertaleSerializationException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace UndertaleModLib 4 | { 5 | [Serializable] 6 | internal class UndertaleSerializationException : Exception 7 | { 8 | public UndertaleSerializationException() 9 | { 10 | } 11 | 12 | public UndertaleSerializationException(string message) : base(message) 13 | { 14 | } 15 | 16 | public UndertaleSerializationException(string message, Exception innerException) : base(message, innerException) 17 | { 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /UndertaleModLib/Util/DebugUtil.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace UndertaleModLib.Util 4 | { 5 | public class DebugUtil 6 | { 7 | 8 | /// 9 | /// Asserts that a specified condition is true. 10 | /// 11 | /// The condition to assert for. 12 | /// The message to use if the assertion fails. 13 | /// Gets thrown if the assertion fails. 14 | public static void Assert(bool expr, string msg = "Unknown error.") 15 | { 16 | if (expr) 17 | return; 18 | 19 | throw new Exception($"Assertion failed! {msg}"); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /UndertaleModLib/Util/FileBinaryWriter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Buffers.Binary; 3 | using System.IO; 4 | using System.Text; 5 | 6 | namespace UndertaleModLib.Util 7 | { 8 | public class FileBinaryWriter : BinaryWriter 9 | { 10 | private readonly Encoding encoding = new UTF8Encoding(false); 11 | 12 | public uint Position 13 | { 14 | get => (uint)OutStream.Position; 15 | set => OutStream.Position = value; 16 | } 17 | 18 | public Encoding Encoding { get => encoding; } 19 | 20 | public FileBinaryWriter(Stream stream, Encoding encoding = null) : base(stream) 21 | { 22 | if (encoding is not null) 23 | this.encoding = encoding; 24 | } 25 | 26 | public void Write(MemoryStream value) 27 | { 28 | value.CopyTo(OutStream); 29 | } 30 | 31 | public override void Write(char[] value) 32 | { 33 | foreach (char c in value) 34 | OutStream.WriteByte(Convert.ToByte(c)); 35 | } 36 | 37 | public void WriteInt24(int value) 38 | { 39 | Span buffer = stackalloc byte[3]; 40 | buffer[0] = (byte)(value & 0xFF); 41 | buffer[1] = (byte)((value >> 8) & 0xFF); 42 | buffer[2] = (byte)((value >> 16) & 0xFF); 43 | OutStream.Write(buffer); 44 | } 45 | 46 | public void WriteUInt24(uint value) 47 | { 48 | Span buffer = stackalloc byte[3]; 49 | buffer[0] = (byte)(value & 0xFF); 50 | buffer[1] = (byte)((value >> 8) & 0xFF); 51 | buffer[2] = (byte)((value >> 16) & 0xFF); 52 | OutStream.Write(buffer); 53 | } 54 | 55 | public void WriteGMString(string value) 56 | { 57 | int len = encoding.GetByteCount(value); 58 | Span buf = stackalloc byte[4]; 59 | BinaryPrimitives.WriteInt32LittleEndian(buf, len); 60 | OutStream.Write(buf); 61 | 62 | if (len > 1024) 63 | OutStream.Write(encoding.GetBytes(value)); 64 | else 65 | { 66 | Span stringBuf = stackalloc byte[len]; 67 | encoding.GetBytes(value.AsSpan(), stringBuf); 68 | OutStream.Write(stringBuf); 69 | } 70 | OutStream.WriteByte(0); 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /UndertaleModLib/Util/GitVersion.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Reflection; 4 | 5 | namespace UndertaleModLib.Util; 6 | 7 | /// 8 | /// Includes miscellaneous git information about the project to compile. 9 | /// Only intended for Debug use! 10 | /// 11 | public static class GitVersion 12 | { 13 | /// 14 | /// Gets and returns the git commit and branch name. 15 | /// 16 | /// The git commit and branch name. 17 | public static string GetGitVersion() 18 | { 19 | string gitOutput; 20 | 21 | // try to access the embedded resource 22 | try 23 | { 24 | var assembly = Assembly.GetExecutingAssembly(); 25 | var resourceName = "UndertaleModLib.gitversion.txt"; 26 | 27 | using (Stream stream = assembly.GetManifestResourceStream(resourceName)) 28 | using (StreamReader reader = new StreamReader(stream)) 29 | { 30 | // \r is getting nuked just in case Windows is weird. 31 | gitOutput = reader.ReadToEnd().Trim().Replace("\r", ""); 32 | } 33 | 34 | // gets formatted as " ()" 35 | var outputAsArray = gitOutput.Split('\n'); 36 | gitOutput = $"{outputAsArray[0]} ({outputAsArray[1]})"; 37 | } 38 | // If accessing it fails, give it a default output 39 | catch 40 | { 41 | gitOutput = "unknownGitCommit"; 42 | } 43 | 44 | // return combined commit + branch 45 | if (String.IsNullOrWhiteSpace(gitOutput)) gitOutput = "unknownGitCommit"; 46 | return gitOutput; 47 | } 48 | } -------------------------------------------------------------------------------- /UndertaleModLibTests/Models/EmbeddedAudioTest.cs: -------------------------------------------------------------------------------- 1 | using UndertaleModLib; 2 | using UndertaleModLib.Models; 3 | using Xunit; 4 | using Xunit.Extensions; 5 | 6 | namespace UndertaleModLibTests.Models; 7 | 8 | public class EmbeddedAudioTest 9 | { 10 | [Theory] 11 | [InlineData(new byte[] 12 | { 13 | 4, 0, 0, 0, 14 | 252, 253, 254, 255, 15 | })] 16 | 17 | public void TestUnserialize(byte[] data) 18 | { 19 | using var stream = new MemoryStream(data); 20 | var reader = new UndertaleReader(stream); 21 | var embeddedAudio = new UndertaleEmbeddedAudio(); 22 | 23 | embeddedAudio.Unserialize(reader); 24 | 25 | Assert.True(embeddedAudio.Data.Length == BitConverter.ToInt32(data[..4])); 26 | Assert.Equal(embeddedAudio.Data, data[4..]); 27 | } 28 | 29 | [Fact] 30 | public void TestSerialize() 31 | { 32 | using var stream = new MemoryStream(); 33 | var fullData = new byte[] { 4, 0, 0, 0, 252, 253, 254, 255 }; 34 | UndertaleEmbeddedAudio audio = new UndertaleEmbeddedAudio() 35 | { 36 | Name = new UndertaleString("foobar"), 37 | Data = fullData[4..] 38 | }; 39 | var writer = new UndertaleWriter(stream); 40 | 41 | audio.Serialize(writer); 42 | 43 | Assert.True(stream.Length == fullData.Length); 44 | Assert.Equal(stream.ToArray(), fullData); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /UndertaleModLibTests/UndertaleModLibTests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | 8 | false 9 | 10 | 11 | 12 | 13 | 14 | 15 | runtime; build; native; contentfiles; analyzers; buildtransitive 16 | all 17 | 18 | 19 | runtime; build; native; contentfiles; analyzers; buildtransitive 20 | all 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /UndertaleModLibTests/Util/MurmurHashTest.cs: -------------------------------------------------------------------------------- 1 | using Xunit; 2 | using UndertaleModLib.Util; 3 | 4 | namespace UndertaleModLibTests.Util; 5 | 6 | // Test data from https://en.wikipedia.org/wiki/MurmurHash. 7 | public class MurmurHashTest 8 | { 9 | [Theory] 10 | [InlineData("", 0, 0)] 11 | [InlineData("", 1, 0x514e28b7)] 12 | [InlineData("", 0xffffffff, 0x81f16f39)] 13 | [InlineData("test", 0, 0xba6bd213)] 14 | [InlineData("test", 0x9747b28c, 0x704b81dc)] 15 | [InlineData("Hello, world!", 0, 0xc0363e43)] 16 | [InlineData("Hello, world!", 0x9747b28c, 0x24884cba)] 17 | [InlineData("The quick brown fox jumps over the lazy dog", 0, 0x2e4ff723)] 18 | [InlineData("The quick brown fox jumps over the lazy dog", 0x9747b28c, 0x2fa826cd)] 19 | public void TestHashStrings(string str, uint seed, uint expectedHash) 20 | { 21 | Assert.Equal(expectedHash, MurmurHash.Hash(str, seed)); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /UndertaleModTests/GamePaths.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Security.Cryptography; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | using UndertaleModLib; 10 | 11 | namespace UndertaleModTests 12 | { 13 | internal static class GamePaths 14 | { 15 | public static string UNDERTALE_PATH = @"C:\Program Files (x86)\Steam\steamapps\common\Undertale\data.win"; 16 | public static string UNDERTALE_MD5 = "5903fc5cb042a728d4ad8ee9e949c6eb"; 17 | public static string UNDERTALE_SWITCH_PATH = @"..\..\..\Test\bin\Debug\switch\game.win"; 18 | public static string UNDERTALE_SWITCH_MD5 = "427520a97db28c87da4220abb3a334c1"; 19 | public static string DELTARUNE_PATH = @"C:\Program Files (x86)\SURVEY_PROGRAM\data.win"; 20 | public static string DELTARUNE_MD5 = "a88a2db3a68c714ca2b1ff57ac08a032"; 21 | } 22 | 23 | public abstract class GameTestBase 24 | { 25 | protected readonly string path; 26 | protected readonly string expectedMD5; 27 | protected UndertaleData data; 28 | 29 | protected static string GenerateMD5(Stream stream) 30 | { 31 | using (var md5 = MD5.Create()) 32 | { 33 | var hash = md5.ComputeHash(stream); 34 | return BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant(); 35 | } 36 | } 37 | 38 | public GameTestBase(string path, string md5) 39 | { 40 | this.path = path; 41 | this.expectedMD5 = md5; 42 | } 43 | 44 | [TestInitialize] 45 | public void LoadData() 46 | { 47 | if (!File.Exists(path)) 48 | Assert.Inconclusive("Unable to test, file not found: " + path); 49 | 50 | using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read)) 51 | { 52 | string fileMD5 = GenerateMD5(fs); 53 | if (fileMD5 != expectedMD5) 54 | Assert.Inconclusive("Unable to test, incorrect file: got " + fileMD5 + " expected " + expectedMD5); 55 | fs.Position = 0; 56 | 57 | data = UndertaleIO.Read(fs); 58 | } 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /UndertaleModTests/UndertaleModTests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net8.0 4 | Library 5 | false 6 | AnyCPU;x64 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 4.13.0 15 | 16 | 17 | 3.8.3 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /UndertaleModTool/App.xaml: -------------------------------------------------------------------------------- 1 |  6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /UndertaleModTool/App.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Configuration; 5 | using System.Data; 6 | using System.Linq; 7 | using System.Threading.Tasks; 8 | using System.Windows; 9 | using System.Windows.Media; 10 | 11 | namespace UndertaleModTool 12 | { 13 | /// 14 | /// Logika interakcji dla klasy App.xaml 15 | /// 16 | public partial class App : Application 17 | { 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /UndertaleModTool/Controls/ColorPicker.xaml: -------------------------------------------------------------------------------- 1 |  9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /UndertaleModTool/Controls/DataUserControl.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | 3 | namespace UndertaleModTool 4 | { 5 | public partial class DataUserControl : System.Windows.Controls.UserControl 6 | { 7 | public DataUserControl() 8 | { 9 | } 10 | 11 | protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e) 12 | { 13 | // prevent WPF binding errors (and unnecessary "DataContextChanged" firing) when switching to incompatible data type 14 | if (e.NewValue is null && e.Property == DataContextProperty) 15 | return; 16 | 17 | base.OnPropertyChanged(e); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /UndertaleModTool/Controls/FlagsBox.xaml: -------------------------------------------------------------------------------- 1 |  10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /UndertaleModTool/Controls/FlagsBox.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Linq; 4 | using System.Windows; 5 | using System.Windows.Controls; 6 | using System.Windows.Data; 7 | 8 | namespace UndertaleModTool 9 | { 10 | /// 11 | /// Interaction logic for FlagsBox.xaml 12 | /// 13 | public partial class FlagsBox : UserControl 14 | { 15 | public object Value 16 | { 17 | get { return (object)GetValue(ValueProperty); } 18 | set { SetValue(ValueProperty, value); } 19 | } 20 | 21 | public static readonly DependencyProperty ValueProperty = 22 | DependencyProperty.Register("Value", typeof(object), 23 | typeof(FlagsBox), 24 | new FrameworkPropertyMetadata(null, 25 | FrameworkPropertyMetadataOptions.BindsTwoWayByDefault)); 26 | 27 | public FlagsBox() 28 | { 29 | InitializeComponent(); 30 | } 31 | } 32 | 33 | public class EnumToValuesConverter : IValueConverter 34 | { 35 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 36 | { 37 | if (value is null) 38 | { 39 | return Array.Empty(); 40 | } 41 | return Enum.GetValues(value.GetType()); 42 | } 43 | 44 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 45 | { 46 | return null; 47 | } 48 | } 49 | 50 | public class EnumFlagToBoolConverter : IMultiValueConverter 51 | { 52 | dynamic enumValue; 53 | dynamic flag; 54 | 55 | public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) 56 | { 57 | if (values.Any(x => x == DependencyProperty.UnsetValue)) 58 | return DependencyProperty.UnsetValue; 59 | 60 | enumValue = (Enum)values[0]; 61 | flag = (Enum)values[1]; 62 | 63 | return enumValue.HasFlag(flag); 64 | } 65 | 66 | public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) 67 | { 68 | if ((bool)value) 69 | { 70 | enumValue |= flag; 71 | } 72 | else 73 | { 74 | enumValue &= ~flag; 75 | } 76 | return new object[] { enumValue, flag }; 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /UndertaleModTool/Controls/System/ButtonDark.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | using System.Windows.Media; 3 | 4 | namespace UndertaleModTool 5 | { 6 | /// 7 | /// A standard button which compatible with the dark mode. 8 | /// 9 | public partial class ButtonDark : System.Windows.Controls.Button 10 | { 11 | // Setting "Foreground" implicitly breaks internal "IsEnabled" style trigger, 12 | // so this has to be handled manually. 13 | private static readonly SolidColorBrush disabledTextBrush = new(Color.FromArgb(255, 131, 131, 131)); 14 | 15 | /// Initializes a new instance of the button. 16 | public ButtonDark() 17 | { 18 | // Even though this will be called again in "OnPropertyChanged()", it's required. 19 | SetResourceReference(ForegroundProperty, "CustomTextBrush"); 20 | } 21 | 22 | /// 23 | protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e) 24 | { 25 | if (e.Property == IsEnabledProperty) 26 | { 27 | if ((bool)e.NewValue) 28 | SetResourceReference(ForegroundProperty, "CustomTextBrush"); 29 | else 30 | Foreground = disabledTextBrush; 31 | } 32 | 33 | base.OnPropertyChanged(e); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /UndertaleModTool/Controls/System/ComboBoxDark.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows; 3 | using System.Windows.Controls; 4 | using System.Windows.Controls.Primitives; 5 | using System.Windows.Media; 6 | 7 | namespace UndertaleModTool 8 | { 9 | /// 10 | /// A standard combo box which compatible with the dark mode. 11 | /// 12 | public partial class ComboBoxDark : ComboBox 13 | { 14 | // Setting "Foreground" implicitly breaks internal "IsEnabled" style trigger, 15 | // so this has to be handled manually. 16 | private static readonly SolidColorBrush disabledTextBrush = new(Color.FromArgb(255, 131, 131, 131)); 17 | 18 | /// Initializes a new instance of the combo box. 19 | public ComboBoxDark() 20 | { 21 | // Even though this will be called again in "OnPropertyChanged()", it's required. 22 | SetResourceReference(ForegroundProperty, "CustomTextBrush"); 23 | 24 | Loaded += ComboBox_Loaded; 25 | } 26 | 27 | private void ComboBox_Loaded(object sender, RoutedEventArgs e) 28 | { 29 | Popup popup = MainWindow.FindVisualChild(this); 30 | var content = MainWindow.FindVisualChild(popup?.Child); 31 | if (content is null) 32 | return; 33 | 34 | // Change text color of dropdown items 35 | content.SetResourceReference(ForegroundProperty, SystemColors.ControlTextBrushKey); 36 | } 37 | 38 | /// 39 | protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e) 40 | { 41 | if (e.Property == IsEnabledProperty) 42 | { 43 | if ((bool)e.NewValue) 44 | SetResourceReference(ForegroundProperty, "CustomTextBrush"); 45 | else 46 | Foreground = disabledTextBrush; 47 | } 48 | 49 | base.OnPropertyChanged(e); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /UndertaleModTool/Controls/System/DataGridDark.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using System.Windows; 3 | using System.Windows.Controls; 4 | using System.Windows.Controls.Primitives; 5 | using System.Windows.Threading; 6 | 7 | namespace UndertaleModTool 8 | { 9 | /// 10 | /// A standard data grid which compatible with the dark mode. 11 | /// 12 | public partial class DataGridDark : DataGrid 13 | { 14 | /// Initializes a new instance of the data grid. 15 | public DataGridDark() 16 | { 17 | Loaded += DataGrid_Loaded; 18 | AddingNewItem += DataGrid_AddingNewItem; 19 | } 20 | 21 | private void DataGrid_AddingNewItem(object sender, AddingNewItemEventArgs e) 22 | { 23 | _ = Task.Run(() => 24 | { 25 | Dispatcher.Invoke(() => 26 | { 27 | UpdateLayout(); 28 | CommitEdit(DataGridEditingUnit.Row, true); 29 | }); 30 | }); 31 | } 32 | 33 | private void DataGrid_Loaded(object sender, RoutedEventArgs e) 34 | { 35 | var pres = MainWindow.FindVisualChild(this); 36 | if (pres is null) 37 | return; 38 | 39 | pres.SetResourceReference(ForegroundProperty, "CustomTextBrush"); 40 | } 41 | 42 | protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e) 43 | { 44 | if (e.Property == VisibilityProperty) 45 | { 46 | if ((Visibility)e.NewValue == Visibility.Visible) 47 | { 48 | base.OnPropertyChanged(e); 49 | UpdateLayout(); 50 | 51 | var pres = MainWindow.FindVisualChild(this); 52 | pres?.SetResourceReference(ForegroundProperty, "CustomTextBrush"); 53 | 54 | return; 55 | } 56 | } 57 | 58 | base.OnPropertyChanged(e); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /UndertaleModTool/Controls/System/MenuItemDark.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows; 3 | using System.Windows.Controls; 4 | using System.Windows.Controls.Primitives; 5 | using System.Windows.Media; 6 | using System.Windows.Shapes; 7 | 8 | namespace UndertaleModTool 9 | { 10 | /// 11 | /// A standard menu item which compatible with the dark mode. 12 | /// 13 | public partial class MenuItemDark : MenuItem 14 | { 15 | /// Initializes a new instance of the menu item. 16 | public MenuItemDark() 17 | { 18 | Loaded += MenuItemDark_Loaded; 19 | } 20 | 21 | private void MenuItemDark_Loaded(object sender, RoutedEventArgs e) 22 | { 23 | Popup popup = MainWindow.FindVisualChild(this); 24 | var content = popup?.Child as Border; 25 | if (content is null) 26 | return; 27 | 28 | content.SetResourceReference(BackgroundProperty, SystemColors.MenuBrushKey); 29 | } 30 | 31 | /// 32 | public override void OnApplyTemplate() 33 | { 34 | var rightArrow = MainWindow.FindVisualChild(this, "RightArrow"); 35 | rightArrow?.SetResourceReference(Path.FillProperty, SystemColors.MenuTextBrushKey); 36 | 37 | base.OnApplyTemplate(); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /UndertaleModTool/Controls/System/RepeatButtonDark.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | using System.Windows.Media; 3 | 4 | namespace UndertaleModTool 5 | { 6 | /// 7 | /// A standard repeat button which compatible with the dark mode. 8 | /// 9 | public partial class RepeatButtonDark : System.Windows.Controls.Primitives.RepeatButton 10 | { 11 | // Setting "Foreground" implicitly breaks internal "IsEnabled" style trigger, 12 | // so this has to be handled manually. 13 | private static readonly SolidColorBrush disabledTextBrush = new(Color.FromArgb(255, 131, 131, 131)); 14 | 15 | /// Initializes a new instance of the button. 16 | public RepeatButtonDark() 17 | { 18 | // Even though this will be called again in "OnPropertyChanged()", it's required. 19 | SetResourceReference(ForegroundProperty, "CustomTextBrush"); 20 | } 21 | 22 | /// 23 | protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e) 24 | { 25 | if (e.Property == IsEnabledProperty) 26 | { 27 | if ((bool)e.NewValue) 28 | SetResourceReference(ForegroundProperty, "CustomTextBrush"); 29 | else 30 | Foreground = disabledTextBrush; 31 | } 32 | 33 | base.OnPropertyChanged(e); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /UndertaleModTool/Controls/System/TextBoxDark.cs: -------------------------------------------------------------------------------- 1 | using System.Windows.Controls; 2 | 3 | namespace UndertaleModTool 4 | { 5 | /// 6 | /// A standard text box which compatible with the dark mode. 7 | /// 8 | public partial class TextBoxDark : TextBox 9 | { 10 | /// Initializes a new instance of the text box. 11 | public TextBoxDark() 12 | { 13 | SetResourceReference(ContextMenuProperty, "textBoxContextMenu"); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /UndertaleModTool/Controls/TransparencyGridBrush.xaml: -------------------------------------------------------------------------------- 1 |  3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /UndertaleModTool/Controls/UndertaleTexturePageItemDisplay.xaml: -------------------------------------------------------------------------------- 1 |  10 | 11 | 12 | 13 | 15 | 18 | 19 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /UndertaleModTool/Controls/UndertaleTexturePageItemDisplay.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using System.Windows; 8 | using System.Windows.Controls; 9 | using System.Windows.Data; 10 | using System.Windows.Documents; 11 | using System.Windows.Input; 12 | using System.Windows.Media; 13 | using System.Windows.Media.Imaging; 14 | using System.Windows.Navigation; 15 | using System.Windows.Shapes; 16 | using System.Windows.Threading; 17 | 18 | namespace UndertaleModTool 19 | { 20 | /// 21 | /// Logika interakcji dla klasy UndertaleTexturePageItemDisplay.xaml 22 | /// 23 | public partial class UndertaleTexturePageItemDisplay : UserControl 24 | { 25 | public static readonly DependencyProperty DisplayBorderProperty = 26 | DependencyProperty.Register("DisplayBorder", typeof(bool), 27 | typeof(UndertaleTexturePageItemDisplay), 28 | new FrameworkPropertyMetadata(true, 29 | FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, (sender, e) => 30 | { 31 | var inst = sender as UndertaleTexturePageItemDisplay; 32 | if (inst is null) 33 | return; 34 | if (e.NewValue is not bool val) 35 | return; 36 | 37 | inst.RenderAreaBorder.BorderThickness = new Thickness(val ? 1 : 0); 38 | })); 39 | public bool DisplayBorder 40 | { 41 | get { return (bool)GetValue(DisplayBorderProperty); } 42 | set { SetValue(DisplayBorderProperty, value); } 43 | } 44 | 45 | public UndertaleTexturePageItemDisplay() 46 | { 47 | InitializeComponent(); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /UndertaleModTool/Converters/BooleanToVisibilityConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Windows; 4 | using System.Windows.Data; 5 | 6 | namespace UndertaleModTool 7 | { 8 | public sealed class BooleanToVisibilityConverter : IValueConverter 9 | { 10 | public Visibility trueValue { get; set; } 11 | public Visibility falseValue { get; set; } 12 | 13 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 14 | { 15 | return (value is bool boolean && boolean) ? trueValue : falseValue; 16 | } 17 | 18 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 19 | { 20 | throw new NotImplementedException(); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /UndertaleModTool/Converters/ByteArrayConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using System.Windows; 8 | using System.Windows.Data; 9 | 10 | namespace UndertaleModTool 11 | { 12 | [ValueConversion(typeof(byte[]), typeof(string))] 13 | public sealed class ByteArrayConverter : IValueConverter 14 | { 15 | public byte[] loaded_for_edit = new byte[16]; 16 | public byte[] bytes = new byte[16]; 17 | public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 18 | { 19 | try 20 | { 21 | loaded_for_edit = (byte[])value; 22 | return BitConverter.ToString((byte[])value).Replace("-", " "); 23 | } 24 | catch 25 | { 26 | return "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00"; 27 | } 28 | } 29 | public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 30 | { 31 | try 32 | { 33 | String hex = (String)value; 34 | String[] hex_values = hex.Split(" "); 35 | if (hex_values.Length != 16) 36 | return loaded_for_edit; 37 | bytes = new byte[hex_values.Length]; 38 | for (int i = 0; i < hex_values.Length; i += 1) 39 | bytes[i] = System.Convert.ToByte(hex_values[i], 16); 40 | return bytes; 41 | } 42 | catch 43 | { 44 | return loaded_for_edit; 45 | } 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /UndertaleModTool/Converters/CompareNumbersConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using System.Windows; 8 | using System.Windows.Data; 9 | 10 | namespace UndertaleModTool 11 | { 12 | public class CompareNumbersConverter : IMultiValueConverter 13 | { 14 | // these could be overridden on declaration 15 | public object TrueValue { get; set; } = Visibility.Visible; 16 | public object FalseValue { get; set; } = Visibility.Collapsed; 17 | 18 | public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) 19 | { 20 | double a, b; 21 | try 22 | { 23 | a = (double)values[0]; 24 | b = (double)values[1]; 25 | } 26 | catch 27 | { 28 | return null; 29 | } 30 | 31 | if (parameter is string par) 32 | { 33 | int r; 34 | if (par == ">") // greater than 35 | r = 1; 36 | else if (par == "<") // less than 37 | r = -1; 38 | else 39 | return null; 40 | 41 | bool res = a.CompareTo(b) == r; 42 | return res ? TrueValue : FalseValue; 43 | } 44 | 45 | return null; 46 | } 47 | 48 | public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) 49 | { 50 | throw new NotImplementedException(); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /UndertaleModTool/Converters/DataFieldOneTimeConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | using System.Linq; 5 | using System.Reflection; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using System.Windows; 9 | using System.Windows.Data; 10 | using UndertaleModLib; 11 | 12 | namespace UndertaleModTool 13 | { 14 | public class DataFieldOneTimeConverter : IValueConverter 15 | { 16 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 17 | { 18 | if (value is not UndertaleData data || parameter is not string par) 19 | return null; 20 | 21 | FieldInfo info = data.GetType().GetField(par); 22 | object resObj = info?.GetValue(data); 23 | 24 | if (resObj is bool res) 25 | return res ? Visibility.Visible : Visibility.Collapsed; 26 | else 27 | return null; 28 | } 29 | 30 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 31 | { 32 | throw new NotImplementedException(); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /UndertaleModTool/Converters/EqualityConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using System.Windows.Data; 8 | 9 | namespace UndertaleModTool 10 | { 11 | public class EqualityConverter : IMultiValueConverter 12 | { 13 | public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) 14 | { 15 | if (values == null) 16 | return values; 17 | 18 | if (values.Length < 2) 19 | return false; 20 | 21 | bool invert = parameter is string par && par == "invert"; 22 | return (values[0] == values[1]) ^ invert; 23 | } 24 | 25 | public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) 26 | { 27 | throw new NotImplementedException(); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /UndertaleModTool/Converters/EventNameConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using System.Windows.Data; 8 | using UndertaleModLib.Models; 9 | 10 | namespace UndertaleModTool 11 | { 12 | [ValueConversion(typeof(uint), typeof(string))] 13 | public class EventNameConverter : IValueConverter 14 | { 15 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 16 | { 17 | if (parameter is string par && par == "EventType") 18 | return Enum.Parse(typeof(EventType), (string)value); 19 | 20 | uint val = System.Convert.ToUInt32(value); 21 | return ((EventType)val).ToString(); 22 | } 23 | 24 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 25 | { 26 | return (uint)(EventType)Enum.Parse(typeof(EventType), (string)value); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /UndertaleModTool/Converters/FilteredViewConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Diagnostics; 5 | using System.Globalization; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | using System.Windows; 10 | using System.Windows.Data; 11 | using UndertaleModLib; 12 | 13 | namespace UndertaleModTool 14 | { 15 | [ValueConversion(typeof(object), typeof(ICollectionView))] 16 | public class FilteredViewConverter : DependencyObject, IValueConverter 17 | { 18 | public static DependencyProperty FilterProperty = 19 | DependencyProperty.Register("Filter", typeof(string), 20 | typeof(FilteredViewConverter), 21 | new FrameworkPropertyMetadata(null)); 22 | 23 | public string Filter 24 | { 25 | get { return (string)GetValue(FilterProperty); } 26 | set { SetValue(FilterProperty, value); } 27 | } 28 | 29 | protected virtual Predicate CreateFilter() 30 | { 31 | return (obj) => 32 | { 33 | if (String.IsNullOrEmpty(Filter)) 34 | return true; 35 | if (obj is ISearchable) 36 | return (obj as ISearchable)?.SearchMatches(Filter) ?? false; 37 | if (obj is UndertaleNamedResource) 38 | return ((obj as UndertaleNamedResource)?.Name?.Content?.IndexOf(Filter, StringComparison.OrdinalIgnoreCase) ?? -1) >= 0; 39 | if (obj is object[] links) 40 | return links.Select(x => x is UndertaleNamedResource res ? res.Name?.Content : x.ToString()) 41 | .Any(x => (x?.IndexOf(Filter, StringComparison.OrdinalIgnoreCase) ?? -1) >= 0); 42 | return true; 43 | }; 44 | } 45 | 46 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 47 | { 48 | if (value is null) 49 | return null; 50 | ICollectionView filteredView = CollectionViewSource.GetDefaultView(value); 51 | filteredView.Filter = CreateFilter(); 52 | return filteredView; 53 | } 54 | 55 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 56 | { 57 | throw new NotImplementedException(); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /UndertaleModTool/Converters/ForwardButtonEnabledConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Windows.Data; 4 | 5 | namespace UndertaleModTool 6 | { 7 | public class ForwardButtonEnabledConverter : IMultiValueConverter 8 | { 9 | public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) 10 | { 11 | if (values[0] is not int pos || values[1] is not int count) 12 | return false; 13 | 14 | return pos != count - 1; 15 | } 16 | 17 | public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) 18 | { 19 | throw new NotImplementedException(); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /UndertaleModTool/Converters/GameObjectByIdConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using System.Windows; 8 | using System.Windows.Data; 9 | using UndertaleModLib.Models; 10 | 11 | namespace UndertaleModTool 12 | { 13 | [ValueConversion(typeof(uint), typeof(UndertaleGameObject))] 14 | public class GameObjectByIdConverter : IValueConverter 15 | { 16 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 17 | { 18 | uint val = System.Convert.ToUInt32(value); 19 | UndertaleGameObject returnObj = null; 20 | if (val < (Application.Current.MainWindow as MainWindow).Data.GameObjects.Count) 21 | { 22 | returnObj = (Application.Current.MainWindow as MainWindow).Data.GameObjects[(int)val]; 23 | return returnObj; 24 | } 25 | else 26 | { 27 | return returnObj; 28 | } 29 | } 30 | 31 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 32 | { 33 | return (uint)(Application.Current.MainWindow as MainWindow).Data.GameObjects.IndexOf((UndertaleGameObject)value); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /UndertaleModTool/Converters/GameObjectToStringConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Windows.Data; 4 | using static UndertaleModLib.Models.UndertaleRoom; 5 | 6 | namespace UndertaleModTool 7 | { 8 | public sealed class GameObjectToStringConverter : IMultiValueConverter 9 | { 10 | public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) 11 | { 12 | if (values[0] is not GameObject gameObject) 13 | { 14 | return "(null)"; 15 | } 16 | return gameObject.ToString(); 17 | } 18 | 19 | public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) 20 | { 21 | return null; 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /UndertaleModTool/Converters/GridConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Globalization; 4 | using System.Windows; 5 | using System.Windows.Data; 6 | 7 | namespace UndertaleModTool 8 | { 9 | public class GridConverter : IMultiValueConverter 10 | { 11 | public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) 12 | { 13 | if (values.Any(x => x is not double)) 14 | return new Rect(); 15 | 16 | return new Rect(0, 0, (double)values[0], (double)values[1]); 17 | } 18 | 19 | public object[] ConvertBack(object value, Type[] targetType, object parameter, CultureInfo culture) 20 | { 21 | return null; 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /UndertaleModTool/Converters/ImplementsInterfaceConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Globalization; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using System.Windows.Data; 9 | 10 | namespace UndertaleModTool 11 | { 12 | [ValueConversion(typeof(object), typeof(bool))] 13 | public class ImplementsInterfaceConverter : IValueConverter 14 | { 15 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 16 | { 17 | Type iface = (Type)parameter; 18 | return iface.IsAssignableFrom(value?.GetType()); 19 | } 20 | 21 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 22 | { 23 | throw new NotImplementedException(); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /UndertaleModTool/Converters/InvertBooleanConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Windows.Data; 4 | 5 | namespace UndertaleModTool 6 | { 7 | public sealed class InvertBooleanConverter : IValueConverter 8 | { 9 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 10 | { 11 | return value is not bool boolean || !boolean; 12 | } 13 | 14 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 15 | { 16 | return value is not bool boolean || !boolean; 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /UndertaleModTool/Converters/IsNullConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Windows.Data; 4 | 5 | namespace UndertaleModTool 6 | { 7 | public class IsNullConverter : IValueConverter 8 | { 9 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 10 | { 11 | bool invert = parameter is string par && par == "True"; 12 | return (value is null) ^ invert; 13 | } 14 | 15 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 16 | { 17 | throw new NotImplementedException(); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /UndertaleModTool/Converters/IsVersionAtLeastConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Text.RegularExpressions; 7 | using System.Windows; 8 | using System.Windows.Data; 9 | 10 | namespace UndertaleModTool 11 | { 12 | public class IsVersionAtLeastConverter : IValueConverter 13 | { 14 | private static readonly MainWindow mainWindow = Application.Current.MainWindow as MainWindow; 15 | private static readonly Regex versionRegex = new(@"(\d+)\.(\d+)(?:\.(\d+))?(?:\.(\d+))?", RegexOptions.Compiled); 16 | 17 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 18 | { 19 | if (mainWindow is null 20 | || mainWindow.Data?.GeneralInfo is null 21 | || parameter is not string verStr 22 | || verStr.Length == 0) 23 | return Visibility.Hidden; 24 | 25 | var ver = versionRegex.Match(verStr); 26 | if (!ver.Success) 27 | return Visibility.Hidden; 28 | try 29 | { 30 | uint major = uint.Parse(ver.Groups[1].Value); 31 | uint minor = uint.Parse(ver.Groups[2].Value); 32 | uint release = 0; 33 | uint build = 0; 34 | if (ver.Groups[3].Value != "") 35 | release = uint.Parse(ver.Groups[3].Value); 36 | if (ver.Groups[4].Value != "") 37 | build = uint.Parse(ver.Groups[4].Value); 38 | 39 | if (mainWindow.Data.IsVersionAtLeast(major, minor, release, build)) 40 | return Visibility.Visible; 41 | else 42 | return Visibility.Collapsed; 43 | } 44 | catch 45 | { 46 | return Visibility.Hidden; 47 | } 48 | } 49 | 50 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 51 | { 52 | throw new NotImplementedException(); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /UndertaleModTool/Converters/MaskImageConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Windows; 7 | using System.Windows.Data; 8 | using System.Windows.Media; 9 | using System.Windows.Media.Imaging; 10 | using UndertaleModLib.Models; 11 | 12 | namespace UndertaleModTool 13 | { 14 | public class MaskImageConverter : IMultiValueConverter 15 | { 16 | public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture) 17 | { 18 | if (values.Any(e => e == DependencyProperty.UnsetValue)) 19 | { 20 | return null; 21 | } 22 | 23 | int width = (int)values[0]; 24 | int height = (int)values[1]; 25 | byte[] data = (byte[])values[2]; 26 | if (data == null || data.Length != (width + 7) / 8 * height || width <= 0 || height <= 0) 27 | return null; 28 | return BitmapSource.Create(width, height, 96, 96, PixelFormats.BlackWhite, null, data, ((width + 7) / 8)); 29 | } 30 | 31 | public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture) 32 | { 33 | throw new NotSupportedException(); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /UndertaleModTool/Converters/NegateNumberConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Windows.Data; 4 | 5 | namespace UndertaleModTool 6 | { 7 | public class NegateNumberConverter : IValueConverter 8 | { 9 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 10 | { 11 | double num; 12 | try 13 | { 14 | num = -1 * System.Convert.ToDouble(value); 15 | } 16 | catch 17 | { 18 | return null; 19 | } 20 | 21 | if (parameter is string par) 22 | { 23 | object res = null; 24 | 25 | try 26 | { 27 | res = par switch 28 | { 29 | "sbyte" => (sbyte)num, 30 | "short" => (short)num, 31 | "int" => (int)num, 32 | "long" => (long)num, 33 | "float" => (float)num, 34 | "decimal" => (decimal)num, 35 | _ => null 36 | }; 37 | } 38 | catch { } 39 | 40 | return res; 41 | } 42 | else 43 | return num; 44 | } 45 | 46 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 47 | { 48 | throw new NotImplementedException(); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /UndertaleModTool/Converters/NullToVisibilityConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using System.Windows; 8 | using System.Windows.Data; 9 | 10 | namespace UndertaleModTool 11 | { 12 | public sealed class NullToVisibilityConverter : IValueConverter 13 | { 14 | public Visibility nullValue { get; set; } 15 | public Visibility notNullValue { get; set; } 16 | 17 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 18 | { 19 | return value == null ? nullValue : notNullValue; 20 | } 21 | 22 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 23 | { 24 | throw new NotImplementedException(); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /UndertaleModTool/Converters/RectConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using System.Windows; 8 | using System.Windows.Data; 9 | 10 | namespace UndertaleModTool 11 | { 12 | public class RectConverter : IMultiValueConverter 13 | { 14 | public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) 15 | { 16 | bool ignore = parameter is string par && par == "returnEmptyOnNull"; 17 | 18 | if (values.Any(e => e == DependencyProperty.UnsetValue)) 19 | { 20 | if (ignore) 21 | return new Rect(0, 0, 0, 0); 22 | else 23 | return null; 24 | } 25 | 26 | return new Rect((ushort)values[0], (ushort)values[1], (ushort)values[2], (ushort)values[3]); 27 | } 28 | 29 | public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) 30 | { 31 | throw new NotSupportedException(); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /UndertaleModTool/Converters/ResourceTreeFilteredViewConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | using System.Windows.Data; 4 | 5 | namespace UndertaleModTool 6 | { 7 | [ValueConversion(typeof(object), typeof(ICollectionView))] 8 | public class ResourceTreeFilteredViewConverter : FilteredViewConverter 9 | { 10 | protected override Predicate CreateFilter() 11 | { 12 | Predicate baseFilter = base.CreateFilter(); 13 | return (obj) => 14 | { 15 | if (!Settings.Instance.ShowNullEntriesInResourceTree && obj is null) 16 | return false; 17 | return baseFilter(obj); 18 | }; 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /UndertaleModTool/Converters/StringTitleConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Text.RegularExpressions; 4 | using System.Windows.Data; 5 | 6 | namespace UndertaleModTool 7 | { 8 | public class StringTitleConverter : IValueConverter 9 | { 10 | public static readonly Regex NewLineRegex = new(@"\r\n?|\n", RegexOptions.Compiled); 11 | public static StringTitleConverter Instance { get; } = new(); 12 | 13 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 14 | { 15 | if (value is not string str) 16 | return null; 17 | 18 | if (str.Length == 0) 19 | return "(empty string)"; 20 | 21 | if (str.Length > 256) 22 | str = str[..256] + "..."; 23 | str = NewLineRegex.Replace(str, " "); 24 | 25 | return str; 26 | } 27 | 28 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 29 | { 30 | throw new NotImplementedException(); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /UndertaleModTool/Converters/SumRectConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using System.Windows; 8 | using System.Windows.Data; 9 | 10 | namespace UndertaleModTool 11 | { 12 | public class SumRectConverter : IMultiValueConverter 13 | { 14 | public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) 15 | { 16 | if (values.Any(e => e == DependencyProperty.UnsetValue)) 17 | { 18 | return null; 19 | } 20 | return new Rect((ushort)values[0] + (uint)values[1], (ushort)values[2] + (uint)values[3], (uint)values[4], (uint)values[5]); 21 | } 22 | 23 | public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) 24 | { 25 | throw new NotSupportedException(); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /UndertaleModTool/Corrections/README.txt: -------------------------------------------------------------------------------- 1 | This folder is for GML decompiled code corrections. 2 | In older versions of this tool, this contained built-in code corrections for Undertale and Deltarune, but they have been obsoleted by the Underanalyzer decompiler. 3 | -------------------------------------------------------------------------------- /UndertaleModTool/Editors/UndertaleAudioGroupEditor.xaml: -------------------------------------------------------------------------------- 1 |  10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | Name 21 | 22 | 23 | Path (GM 2024.14+) 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /UndertaleModTool/Editors/UndertaleAudioGroupEditor.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Windows; 7 | using System.Windows.Controls; 8 | using System.Windows.Data; 9 | using System.Windows.Documents; 10 | using System.Windows.Input; 11 | using System.Windows.Media; 12 | using System.Windows.Media.Imaging; 13 | using System.Windows.Navigation; 14 | using System.Windows.Shapes; 15 | 16 | namespace UndertaleModTool 17 | { 18 | /// 19 | /// Logika interakcji dla klasy UndertaleAudioGroupEditor.xaml 20 | /// 21 | public partial class UndertaleAudioGroupEditor : DataUserControl 22 | { 23 | public UndertaleAudioGroupEditor() 24 | { 25 | InitializeComponent(); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /UndertaleModTool/Editors/UndertaleCodeLocalsEditor.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Windows; 7 | using System.Windows.Controls; 8 | using System.Windows.Data; 9 | using System.Windows.Documents; 10 | using System.Windows.Input; 11 | using System.Windows.Media; 12 | using System.Windows.Media.Imaging; 13 | using System.Windows.Navigation; 14 | using System.Windows.Shapes; 15 | using UndertaleModLib.Models; 16 | 17 | namespace UndertaleModTool 18 | { 19 | /// 20 | /// Logika interakcji dla klasy UndertaleCodeLocalsEditor.xaml 21 | /// 22 | public partial class UndertaleCodeLocalsEditor : DataUserControl 23 | { 24 | public UndertaleCodeLocalsEditor() 25 | { 26 | InitializeComponent(); 27 | } 28 | 29 | private void DataGrid_AddingNewItem(object sender, AddingNewItemEventArgs e) 30 | { 31 | UndertaleCodeLocals.LocalVar obj = new UndertaleCodeLocals.LocalVar(); 32 | obj.Index = (uint)((sender as DataGrid).ItemsSource as IList).Count; 33 | e.NewItem = obj; 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /UndertaleModTool/Editors/UndertaleEmbeddedImageEditor.xaml: -------------------------------------------------------------------------------- 1 |  10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | Name 21 | 22 | 23 | Texture 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /UndertaleModTool/Editors/UndertaleEmbeddedImageEditor.xaml.cs: -------------------------------------------------------------------------------- 1 | using System.Windows.Controls; 2 | 3 | namespace UndertaleModTool 4 | { 5 | /// 6 | /// Logika interakcji dla klasy UndertaleEmbeddedImageEditor.xaml 7 | /// 8 | public partial class UndertaleEmbeddedImageEditor : DataUserControl 9 | { 10 | public UndertaleEmbeddedImageEditor() 11 | { 12 | InitializeComponent(); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /UndertaleModTool/Editors/UndertaleExtensionFileEditor.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Windows; 7 | using System.Windows.Controls; 8 | using System.Windows.Data; 9 | using System.Windows.Documents; 10 | using System.Windows.Input; 11 | using System.Windows.Media; 12 | using System.Windows.Media.Imaging; 13 | using System.Windows.Navigation; 14 | using System.Windows.Shapes; 15 | using UndertaleModLib; 16 | using UndertaleModLib.Models; 17 | 18 | namespace UndertaleModTool 19 | { 20 | /// 21 | /// Interaction logic for UndertaleExtensionFileEditor.xaml 22 | /// 23 | public partial class UndertaleExtensionFileEditor : DataUserControl 24 | { 25 | public UndertaleExtensionFileEditor() 26 | { 27 | InitializeComponent(); 28 | } 29 | 30 | private void DataGrid_AddingNewItem(object sender, AddingNewItemEventArgs e) 31 | { 32 | var itemList = (sender as DataGrid).ItemsSource as IList; 33 | int lastItem = itemList.Count; 34 | 35 | UndertaleExtensionFunction obj = new UndertaleExtensionFunction() 36 | { 37 | Name = (Application.Current.MainWindow as MainWindow).Data.Strings.MakeString($"new_extension_function_{lastItem}"), 38 | ExtName = (Application.Current.MainWindow as MainWindow).Data.Strings.MakeString($"new_extension_function_{lastItem}_ext"), 39 | RetType = UndertaleExtensionVarType.Double, 40 | Arguments = new UndertaleSimpleList(), 41 | Kind = 11, // ??? 42 | ID = (Application.Current.MainWindow as MainWindow).Data.ExtensionFindLastId() 43 | }; 44 | 45 | e.NewItem = obj; 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /UndertaleModTool/Editors/UndertaleExtensionFunctionEditor.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Windows; 7 | using System.Windows.Controls; 8 | using System.Windows.Data; 9 | using System.Windows.Documents; 10 | using System.Windows.Input; 11 | using System.Windows.Media; 12 | using System.Windows.Media.Imaging; 13 | using System.Windows.Navigation; 14 | using System.Windows.Shapes; 15 | 16 | namespace UndertaleModTool 17 | { 18 | /// 19 | /// Логика взаимодействия для UndertaleExtensionFunctionEditor.xaml 20 | /// 21 | public partial class UndertaleExtensionFunctionEditor : DataUserControl 22 | { 23 | public UndertaleExtensionFunctionEditor() 24 | { 25 | InitializeComponent(); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /UndertaleModTool/Editors/UndertaleFunctionEditor.xaml: -------------------------------------------------------------------------------- 1 |  10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | Name 21 | 22 | 23 | Name string ID 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /UndertaleModTool/Editors/UndertaleFunctionEditor.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Windows; 7 | using System.Windows.Controls; 8 | using System.Windows.Data; 9 | using System.Windows.Documents; 10 | using System.Windows.Input; 11 | using System.Windows.Media; 12 | using System.Windows.Media.Imaging; 13 | using System.Windows.Navigation; 14 | using System.Windows.Shapes; 15 | 16 | namespace UndertaleModTool 17 | { 18 | /// 19 | /// Logika interakcji dla klasy UndertaleFunctionEditor.xaml 20 | /// 21 | public partial class UndertaleFunctionEditor : DataUserControl 22 | { 23 | public UndertaleFunctionEditor() 24 | { 25 | InitializeComponent(); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /UndertaleModTool/Editors/UndertaleGameEndEditor.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Windows; 7 | using System.Windows.Controls; 8 | using System.Windows.Data; 9 | using System.Windows.Documents; 10 | using System.Windows.Input; 11 | using System.Windows.Media; 12 | using System.Windows.Media.Imaging; 13 | using System.Windows.Navigation; 14 | using System.Windows.Shapes; 15 | 16 | namespace UndertaleModTool 17 | { 18 | /// 19 | /// Логика взаимодействия для UndertaleGameEndEditor.xaml 20 | /// 21 | public partial class UndertaleGameEndEditor : DataUserControl 22 | { 23 | public UndertaleGameEndEditor() 24 | { 25 | InitializeComponent(); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /UndertaleModTool/Editors/UndertaleGlobalInitEditor.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Windows; 7 | using System.Windows.Controls; 8 | using System.Windows.Data; 9 | using System.Windows.Documents; 10 | using System.Windows.Input; 11 | using System.Windows.Media; 12 | using System.Windows.Media.Imaging; 13 | using System.Windows.Navigation; 14 | using System.Windows.Shapes; 15 | using UndertaleModLib.Models; 16 | 17 | namespace UndertaleModTool 18 | { 19 | /// 20 | /// Interaction logic for UndertaleGlobalInitEditor.xaml 21 | /// 22 | public partial class UndertaleGlobalInitEditor : DataUserControl 23 | { 24 | public UndertaleGlobalInitEditor() 25 | { 26 | InitializeComponent(); 27 | } 28 | private void UndertaleObjectReference_Loaded(object sender, RoutedEventArgs e) 29 | { 30 | var objRef = sender as UndertaleObjectReference; 31 | 32 | objRef.ClearRemoveClickHandler(); 33 | objRef.RemoveButton.Click += Remove_Click_Override; 34 | objRef.RemoveButton.ToolTip = "Remove script"; 35 | objRef.RemoveButton.IsEnabled = true; 36 | } 37 | private void Remove_Click_Override(object sender, RoutedEventArgs e) 38 | { 39 | var btn = (ButtonDark)sender; 40 | var objRef = (UndertaleObjectReference)((Grid)btn.Parent).Parent; 41 | 42 | var data = (GlobalInitEditor)DataContext; 43 | var globalInits = data.GlobalInits; 44 | if (btn.DataContext is not UndertaleGlobalInit) 45 | { 46 | return; 47 | } 48 | globalInits.Remove((UndertaleGlobalInit)btn.DataContext); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /UndertaleModTool/Editors/UndertaleParticleSystemEditor.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace UndertaleModTool 4 | { 5 | /// 6 | /// Interaction logic for UndertaleParticleSystemEditor.xaml 7 | /// 8 | public partial class UndertaleParticleSystemEditor : DataUserControl 9 | { 10 | public UndertaleParticleSystemEditor() 11 | { 12 | InitializeComponent(); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /UndertaleModTool/Editors/UndertaleParticleSystemEmitterEditor.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace UndertaleModTool 4 | { 5 | /// 6 | /// Interaction logic for UndertaleParticleSystemEmitterEditor.xaml 7 | /// 8 | public partial class UndertaleParticleSystemEmitterEditor : DataUserControl 9 | { 10 | public UndertaleParticleSystemEmitterEditor() 11 | { 12 | InitializeComponent(); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /UndertaleModTool/Editors/UndertaleScriptEditor.xaml: -------------------------------------------------------------------------------- 1 |  10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | Name 21 | 22 | 23 | Code 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /UndertaleModTool/Editors/UndertaleScriptEditor.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Windows; 7 | using System.Windows.Controls; 8 | using System.Windows.Data; 9 | using System.Windows.Documents; 10 | using System.Windows.Input; 11 | using System.Windows.Media; 12 | using System.Windows.Media.Imaging; 13 | using System.Windows.Navigation; 14 | using System.Windows.Shapes; 15 | 16 | namespace UndertaleModTool 17 | { 18 | /// 19 | /// Logika interakcji dla klasy UndertaleScriptEditor.xaml 20 | /// 21 | public partial class UndertaleScriptEditor : DataUserControl 22 | { 23 | public UndertaleScriptEditor() 24 | { 25 | InitializeComponent(); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /UndertaleModTool/Editors/UndertaleStringEditor.xaml: -------------------------------------------------------------------------------- 1 |  10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | Content 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /UndertaleModTool/Editors/UndertaleStringEditor.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Windows; 7 | using System.Windows.Controls; 8 | using System.Windows.Data; 9 | using System.Windows.Documents; 10 | using System.Windows.Input; 11 | using System.Windows.Media; 12 | using System.Windows.Media.Imaging; 13 | using System.Windows.Navigation; 14 | using System.Windows.Shapes; 15 | 16 | namespace UndertaleModTool 17 | { 18 | /// 19 | /// Logika interakcji dla klasy UndertaleStringEditor.xaml 20 | /// 21 | public partial class UndertaleStringEditor : DataUserControl 22 | { 23 | public UndertaleStringEditor() 24 | { 25 | InitializeComponent(); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /UndertaleModTool/Editors/UndertaleTextureGroupInfoEditor.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using System.Windows; 8 | using System.Windows.Controls; 9 | using System.Windows.Data; 10 | using System.Windows.Documents; 11 | using System.Windows.Input; 12 | using System.Windows.Media; 13 | using System.Windows.Media.Imaging; 14 | using System.Windows.Navigation; 15 | using System.Windows.Shapes; 16 | using UndertaleModLib; 17 | 18 | namespace UndertaleModTool 19 | { 20 | /// 21 | /// Logika interakcji dla klasy UndertaleTextureGroupInfoEditor.xaml 22 | /// 23 | public partial class UndertaleTextureGroupInfoEditor : DataUserControl 24 | { 25 | public UndertaleTextureGroupInfoEditor() 26 | { 27 | InitializeComponent(); 28 | } 29 | } 30 | 31 | public class IsGM2023Converter : IValueConverter 32 | { 33 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 34 | { 35 | if (value is not UndertaleData data) 36 | return Visibility.Visible; 37 | 38 | return data.IsVersionAtLeast(2023, 1) ? Visibility.Collapsed : Visibility.Visible; 39 | } 40 | 41 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 42 | { 43 | throw new NotImplementedException(); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /UndertaleModTool/Editors/UndertaleTimelineEditor.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Windows; 7 | using System.Windows.Controls; 8 | using System.Windows.Data; 9 | using System.Windows.Documents; 10 | using System.Windows.Input; 11 | using System.Windows.Media; 12 | using System.Windows.Media.Imaging; 13 | using System.Windows.Navigation; 14 | using System.Windows.Shapes; 15 | using UndertaleModLib; 16 | using UndertaleModLib.Models; 17 | 18 | namespace UndertaleModTool 19 | { 20 | /// 21 | /// Interaction logic for UndertaleTimelineEditor.xaml 22 | /// 23 | public partial class UndertaleTimelineEditor : DataUserControl 24 | { 25 | public UndertaleTimelineEditor() 26 | { 27 | InitializeComponent(); 28 | } 29 | 30 | private void DataGrid_AddingNewItem(object sender, AddingNewItemEventArgs e) 31 | { 32 | UndertaleTimeline.UndertaleTimelineMoment obj = new UndertaleTimeline.UndertaleTimelineMoment(); 33 | 34 | // find the last timeline moment (which should have the biggest step value) 35 | var lastMoment = ((sender as DataGrid).ItemsSource as IList).LastOrDefault(); 36 | 37 | // the default value is 0 anyway. 38 | if (lastMoment != null) 39 | obj.Step = lastMoment.Step + 1; 40 | 41 | // make an empty event with a null code entry. 42 | obj.Event = new UndertalePointerList(); 43 | obj.Event.Add(new UndertaleGameObject.EventAction()); 44 | 45 | // we're done here. 46 | e.NewItem = obj; 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /UndertaleModTool/Editors/UndertaleVariableChunkEditor.xaml: -------------------------------------------------------------------------------- 1 |  11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | Instance/global variable count 28 | 29 | 30 | Instance/global variable count (again) 31 | 32 | 33 | Max local variable count 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /UndertaleModTool/Editors/UndertaleVariableChunkEditor.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Windows; 7 | using System.Windows.Controls; 8 | using System.Windows.Data; 9 | using System.Windows.Documents; 10 | using System.Windows.Input; 11 | using System.Windows.Media; 12 | using System.Windows.Media.Imaging; 13 | using System.Windows.Navigation; 14 | using System.Windows.Shapes; 15 | 16 | namespace UndertaleModTool 17 | { 18 | /// 19 | /// Logika interakcji dla klasy UndertaleVariableChunkEditor.xaml 20 | /// 21 | public partial class UndertaleVariableChunkEditor : DataUserControl 22 | { 23 | public UndertaleVariableChunkEditor() 24 | { 25 | InitializeComponent(); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /UndertaleModTool/Editors/UndertaleVariableEditor.xaml: -------------------------------------------------------------------------------- 1 |  11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | Name 32 | 33 | 34 | Instance type 35 | 36 | 37 | VarID 38 | 39 | 40 | Name string ID 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /UndertaleModTool/Editors/UndertaleVariableEditor.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Windows; 7 | using System.Windows.Controls; 8 | using System.Windows.Data; 9 | using System.Windows.Documents; 10 | using System.Windows.Input; 11 | using System.Windows.Media; 12 | using System.Windows.Media.Imaging; 13 | using System.Windows.Navigation; 14 | using System.Windows.Shapes; 15 | 16 | namespace UndertaleModTool 17 | { 18 | /// 19 | /// Logika interakcji dla klasy UndertaleVariableEditor.xaml 20 | /// 21 | public partial class UndertaleVariableEditor : DataUserControl 22 | { 23 | public UndertaleVariableEditor() 24 | { 25 | InitializeComponent(); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /UndertaleModTool/FodyWeavers.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | -------------------------------------------------------------------------------- /UndertaleModTool/NullConditionalDataTemplateSelector.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | using System.Windows.Controls; 3 | 4 | namespace UndertaleModTool 5 | { 6 | /// 7 | /// Selects a DataTemplate based on whether the item is null or not. 8 | /// 9 | /// 10 | /// Used by to select the appropriate template for an item. 11 | /// 12 | public class NullConditionalDataTemplateSelector : DataTemplateSelector 13 | { 14 | /// 15 | /// The template to use if the item is not null. 16 | /// 17 | public DataTemplate NonNullTemplate { get; set; } 18 | 19 | /// 20 | /// The template to use if the item is null. 21 | /// 22 | public DataTemplate NullTemplate { get; set; } 23 | 24 | public override DataTemplate SelectTemplate(object item, DependencyObject container) 25 | { 26 | if (item is null) 27 | return NullTemplate; 28 | return NonNullTemplate; 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /UndertaleModTool/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace UndertaleModTool.Properties { 12 | 13 | 14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.10.0.0")] 16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { 17 | 18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 19 | 20 | public static Settings Default { 21 | get { 22 | return defaultInstance; 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /UndertaleModTool/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /UndertaleModTool/Resources/X.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnderminersTeam/UndertaleModTool/7051c3ac7fa739fe688be4ed7d466bf63050d5b3/UndertaleModTool/Resources/X.png -------------------------------------------------------------------------------- /UndertaleModTool/Resources/X_Down.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnderminersTeam/UndertaleModTool/7051c3ac7fa739fe688be4ed7d466bf63050d5b3/UndertaleModTool/Resources/X_Down.png -------------------------------------------------------------------------------- /UndertaleModTool/Resources/arrow_blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnderminersTeam/UndertaleModTool/7051c3ac7fa739fe688be4ed7d466bf63050d5b3/UndertaleModTool/Resources/arrow_blue.png -------------------------------------------------------------------------------- /UndertaleModTool/Resources/arrow_red.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnderminersTeam/UndertaleModTool/7051c3ac7fa739fe688be4ed7d466bf63050d5b3/UndertaleModTool/Resources/arrow_red.png -------------------------------------------------------------------------------- /UndertaleModTool/Resources/tabs_left_button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnderminersTeam/UndertaleModTool/7051c3ac7fa739fe688be4ed7d466bf63050d5b3/UndertaleModTool/Resources/tabs_left_button.png -------------------------------------------------------------------------------- /UndertaleModTool/Resources/tabs_right_button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnderminersTeam/UndertaleModTool/7051c3ac7fa739fe688be4ed7d466bf63050d5b3/UndertaleModTool/Resources/tabs_right_button.png -------------------------------------------------------------------------------- /UndertaleModTool/Scripts/Resource Exporters/ExportAllAssembly.csx: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | using System; 3 | using System.IO; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | using System.Linq; 7 | 8 | EnsureDataLoaded(); 9 | 10 | if (Data.IsYYC()) 11 | { 12 | ScriptError("The opened game uses YYC: no code is available."); 13 | return; 14 | } 15 | 16 | string codeFolder = PromptChooseDirectory(); 17 | if (codeFolder is null) 18 | { 19 | return; 20 | } 21 | 22 | List toDump = Data.Code.Where(c => c.ParentEntry is null).ToList(); 23 | 24 | SetProgressBar(null, "Code Entries", 0, toDump.Count); 25 | StartProgressBarUpdater(); 26 | 27 | await DumpCode(); 28 | 29 | await StopProgressBarUpdater(); 30 | HideProgressBar(); 31 | 32 | async Task DumpCode() 33 | { 34 | await Task.Run(() => Parallel.ForEach(toDump, DumpCode)); 35 | } 36 | 37 | void DumpCode(UndertaleCode code) 38 | { 39 | if (code is not null) 40 | { 41 | string path = Path.Combine(codeFolder, $"{code.Name.Content}.asm"); 42 | try 43 | { 44 | File.WriteAllText(path, code.Disassemble(Data.Variables, Data.CodeLocals?.For(code))); 45 | } 46 | catch (Exception e) 47 | { 48 | File.WriteAllText(path, $"/*\nDISASSEMBLY FAILED!\n\n{e}\n*/"); 49 | } 50 | } 51 | 52 | IncrementProgressParallel(); 53 | } -------------------------------------------------------------------------------- /UndertaleModTool/Scripts/Resource Exporters/ExportAllCode.csx: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | using System; 3 | using System.IO; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | 9 | EnsureDataLoaded(); 10 | 11 | if (Data.IsYYC()) 12 | { 13 | ScriptError("The opened game uses YYC: no code is available."); 14 | return; 15 | } 16 | 17 | string codeFolder = PromptChooseDirectory(); 18 | if (codeFolder is null) 19 | { 20 | return; 21 | } 22 | 23 | GlobalDecompileContext globalDecompileContext = new(Data); 24 | Underanalyzer.Decompiler.IDecompileSettings decompilerSettings = Data.ToolInfo.DecompilerSettings; 25 | 26 | List toDump = Data.Code.Where(c => c.ParentEntry is null).ToList(); 27 | 28 | SetProgressBar(null, "Code Entries", 0, toDump.Count); 29 | StartProgressBarUpdater(); 30 | 31 | await DumpCode(); 32 | 33 | await StopProgressBarUpdater(); 34 | HideProgressBar(); 35 | 36 | async Task DumpCode() 37 | { 38 | await Task.Run(() => Parallel.ForEach(toDump, DumpCode)); 39 | } 40 | 41 | void DumpCode(UndertaleCode code) 42 | { 43 | if (code is not null) 44 | { 45 | string path = Path.Combine(codeFolder, code.Name.Content + ".gml"); 46 | try 47 | { 48 | File.WriteAllText(path, (code != null 49 | ? new Underanalyzer.Decompiler.DecompileContext(globalDecompileContext, code, decompilerSettings).DecompileToString() 50 | : "")); 51 | } 52 | catch (Exception e) 53 | { 54 | File.WriteAllText(path, "/*\nDECOMPILER FAILED!\n\n" + e.ToString() + "\n*/"); 55 | } 56 | } 57 | 58 | IncrementProgressParallel(); 59 | } 60 | -------------------------------------------------------------------------------- /UndertaleModTool/Scripts/Resource Exporters/ExportAllEmbeddedTextures.csx: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | using System; 3 | using System.IO; 4 | using System.Threading.Tasks; 5 | 6 | EnsureDataLoaded(); 7 | 8 | string texturesFolder = PromptChooseDirectory(); 9 | if (texturesFolder is null) 10 | { 11 | return; 12 | } 13 | 14 | SetProgressBar(null, "Embedded Textures", 0, Data.EmbeddedTextures.Count); 15 | StartProgressBarUpdater(); 16 | 17 | await Task.Run(() => 18 | { 19 | for (int i = 0; i < Data.EmbeddedTextures.Count; i++) 20 | { 21 | try 22 | { 23 | using FileStream fs = new(Path.Combine(texturesFolder, $"{i}.png"), FileMode.Create); 24 | Data.EmbeddedTextures[i].TextureData.Image.SavePng(fs); 25 | } 26 | catch (Exception ex) 27 | { 28 | ScriptMessage($"Failed to export file: {ex.Message}"); 29 | } 30 | 31 | IncrementProgress(); 32 | } 33 | }); 34 | 35 | await StopProgressBarUpdater(); 36 | HideProgressBar(); 37 | -------------------------------------------------------------------------------- /UndertaleModTool/Scripts/Resource Exporters/ExportAllFonts.csx: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | using System; 3 | using System.IO; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | using UndertaleModLib.Util; 7 | using System.Linq; 8 | 9 | EnsureDataLoaded(); 10 | 11 | string fntFolder = PromptChooseDirectory(); 12 | if (fntFolder is null) 13 | { 14 | return; 15 | } 16 | 17 | SetProgressBar(null, "Fonts", 0, Data.Fonts.Count); 18 | StartProgressBarUpdater(); 19 | 20 | TextureWorker worker = null; 21 | using (worker = new()) 22 | { 23 | await DumpFonts(); 24 | } 25 | 26 | await StopProgressBarUpdater(); 27 | HideProgressBar(); 28 | 29 | async Task DumpFonts() 30 | { 31 | await Task.Run(() => Parallel.ForEach(Data.Fonts, DumpFont)); 32 | } 33 | 34 | void DumpFont(UndertaleFont font) 35 | { 36 | if (font is not null) 37 | { 38 | worker.ExportAsPNG(font.Texture, Path.Combine(fntFolder, $"{font.Name.Content}.png")); 39 | using (StreamWriter writer = new(Path.Combine(fntFolder, $"glyphs_{font.Name.Content}.csv"))) 40 | { 41 | writer.WriteLine($"{font.DisplayName};{font.EmSize};{font.Bold};{font.Italic};{font.Charset};{font.AntiAliasing};{font.ScaleX};{font.ScaleY}"); 42 | 43 | foreach (var g in font.Glyphs) 44 | { 45 | writer.WriteLine($"{g.Character};{g.SourceX};{g.SourceY};{g.SourceWidth};{g.SourceHeight};{g.Shift};{g.Offset}"); 46 | } 47 | } 48 | } 49 | 50 | IncrementProgressParallel(); 51 | } 52 | -------------------------------------------------------------------------------- /UndertaleModTool/Scripts/Resource Exporters/ExportAllMasks.csx: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | using System; 3 | using System.IO; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | using UndertaleModLib.Util; 7 | 8 | EnsureDataLoaded(); 9 | 10 | string texFolder = PromptChooseDirectory(); 11 | if (texFolder is null) 12 | { 13 | return; 14 | } 15 | 16 | SetProgressBar(null, "Sprite masks", 0, Data.Sprites.Count); 17 | StartProgressBarUpdater(); 18 | 19 | TextureWorker worker = null; 20 | using (worker = new()) 21 | { 22 | await DumpSprites(); 23 | } 24 | 25 | await StopProgressBarUpdater(); 26 | HideProgressBar(); 27 | 28 | async Task DumpSprites() 29 | { 30 | await Task.Run(() => Parallel.ForEach(Data.Sprites, DumpSprite)); 31 | } 32 | 33 | void DumpSprite(UndertaleSprite sprite) 34 | { 35 | if (sprite is null) 36 | { 37 | return; 38 | } 39 | 40 | for (int i = 0; i < sprite.CollisionMasks.Count; i++) 41 | { 42 | if (sprite.CollisionMasks[i]?.Data is not null) 43 | { 44 | (int maskWidth, int maskHeight) = sprite.CalculateMaskDimensions(Data); 45 | TextureWorker.ExportCollisionMaskPNG(sprite.CollisionMasks[i], Path.Combine(texFolder, $"{sprite.Name.Content}_{i}.png"), maskWidth, maskHeight); 46 | } 47 | } 48 | 49 | IncrementProgressParallel(); 50 | } 51 | -------------------------------------------------------------------------------- /UndertaleModTool/Scripts/Resource Exporters/ExportAllSprites.csx: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | using System; 3 | using System.IO; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | using UndertaleModLib.Util; 7 | 8 | EnsureDataLoaded(); 9 | 10 | string texFolder = PromptChooseDirectory(); 11 | if (texFolder is null) 12 | { 13 | return; 14 | } 15 | 16 | // Prompt for export settings. 17 | bool padded = ScriptQuestion("Export sprites with padding?"); 18 | bool useSubDirectories = ScriptQuestion("Export sprites into subdirectories?"); 19 | 20 | SetProgressBar(null, "Sprites", 0, Data.Sprites.Count); 21 | StartProgressBarUpdater(); 22 | 23 | TextureWorker worker = null; 24 | using (worker = new()) 25 | { 26 | await DumpSprites(); 27 | } 28 | 29 | await StopProgressBarUpdater(); 30 | HideProgressBar(); 31 | 32 | async Task DumpSprites() 33 | { 34 | await Task.Run(() => Parallel.ForEach(Data.Sprites, DumpSprite)); 35 | } 36 | 37 | void DumpSprite(UndertaleSprite sprite) 38 | { 39 | if (sprite is not null) 40 | { 41 | string outputFolder = texFolder; 42 | if (useSubDirectories) 43 | { 44 | outputFolder = Path.Combine(outputFolder, sprite.Name.Content); 45 | if (sprite.Textures.Count > 0) 46 | { 47 | Directory.CreateDirectory(outputFolder); 48 | } 49 | } 50 | 51 | for (int i = 0; i < sprite.Textures.Count; i++) 52 | { 53 | if (sprite.Textures[i]?.Texture is not null) 54 | { 55 | worker.ExportAsPNG(sprite.Textures[i].Texture, Path.Combine(outputFolder, $"{sprite.Name.Content}_{i}.png"), null, padded); 56 | } 57 | } 58 | } 59 | 60 | IncrementProgressParallel(); 61 | } 62 | -------------------------------------------------------------------------------- /UndertaleModTool/Scripts/Resource Exporters/ExportAllStrings.csx: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | using System; 3 | using System.IO; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | using UndertaleModLib.Util; 7 | 8 | EnsureDataLoaded(); 9 | 10 | string stringsPath = PromptSaveFile(".txt", "TXT files (*.txt)|*.txt|All files (*.*)|*.*"); 11 | if (string.IsNullOrWhiteSpace(stringsPath)) 12 | { 13 | return; 14 | } 15 | 16 | bool promptedForNewlines = false; 17 | bool skipNewlines = false; 18 | using (StreamWriter writer = new StreamWriter(stringsPath)) 19 | { 20 | foreach (var str in Data.Strings) 21 | { 22 | if (str.Content.Contains('\n') || str.Content.Contains('\r')) 23 | { 24 | if (!promptedForNewlines) 25 | { 26 | promptedForNewlines = true; 27 | skipNewlines = ScriptQuestion("Export strings containing newlines? Doing so will break reimporting."); 28 | } 29 | if (skipNewlines) 30 | { 31 | continue; 32 | } 33 | } 34 | writer.WriteLine(str.Content); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /UndertaleModTool/Scripts/Resource Exporters/ExportAllStringsJSON.csx: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using System.Text; 3 | 4 | EnsureDataLoaded(); 5 | 6 | string path = PromptSaveFile(".json", "JSON files (*.json)|*.json|TXT files (*.txt)|*.txt|All files (*.*)|*.*"); 7 | if (string.IsNullOrWhiteSpace(path)) 8 | return; 9 | 10 | StringBuilder json = new StringBuilder("{\r\n \"Strings\": [\r\n"); 11 | const string 12 | prefix = " ", 13 | suffix = ",\r\n"; 14 | foreach (string str in Data.Strings.Select(str => str.Content)) 15 | json.Append( 16 | prefix 17 | + JsonifyString(str) 18 | + suffix); 19 | json.Length -= suffix.Length; 20 | json.Append("\r\n ]\r\n}"); 21 | 22 | File.WriteAllText(path, json.ToString()); 23 | ScriptMessage($"Successfully exported to\n{path}"); 24 | 25 | static string JsonifyString(string str) 26 | { 27 | StringBuilder sb = new StringBuilder(); 28 | foreach (char ch in str) 29 | { // Characters that JSON requires escaping 30 | if (ch == '\"') { sb.Append("\\\""); continue; } 31 | if (ch == '\\') { sb.Append("\\\\"); continue; } 32 | if (ch == '\b') { sb.Append("\\b"); continue; } 33 | if (ch == '\f') { sb.Append("\\f"); continue; } 34 | if (ch == '\n') { sb.Append("\\n"); continue; } 35 | if (ch == '\r') { sb.Append("\\r"); continue; } 36 | if (ch == '\t') { sb.Append("\\t"); continue; } 37 | if (Char.IsControl(ch)) 38 | { 39 | sb.Append("\\u" + Convert.ToByte(ch).ToString("x4")); 40 | continue; 41 | } 42 | 43 | sb.Append(ch); 44 | } 45 | return "\"" + sb.ToString() + "\""; 46 | } 47 | -------------------------------------------------------------------------------- /UndertaleModTool/Scripts/Resource Exporters/ExportAllTilesets.csx: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | using System; 3 | using System.IO; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | using UndertaleModLib.Util; 7 | 8 | EnsureDataLoaded(); 9 | 10 | string texFolder = PromptChooseDirectory(); 11 | if (texFolder is null) 12 | { 13 | return; 14 | } 15 | 16 | SetProgressBar(null, "Tilesets", 0, Data.Backgrounds.Count); 17 | StartProgressBarUpdater(); 18 | 19 | TextureWorker worker = null; 20 | using (worker = new()) 21 | { 22 | await DumpTilesets(); 23 | } 24 | 25 | await StopProgressBarUpdater(); 26 | HideProgressBar(); 27 | 28 | async Task DumpTilesets() 29 | { 30 | await Task.Run(() => Parallel.ForEach(Data.Backgrounds, DumpTileset)); 31 | } 32 | 33 | void DumpTileset(UndertaleBackground tileset) 34 | { 35 | if (tileset?.Texture is not null) 36 | { 37 | worker.ExportAsPNG(tileset.Texture, Path.Combine(texFolder, $"{tileset.Name.Content}.png")); 38 | } 39 | 40 | IncrementProgressParallel(); 41 | } -------------------------------------------------------------------------------- /UndertaleModTool/Scripts/Resource Exporters/ExportSpecificSprites.csx: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | using System; 3 | using System.IO; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | using UndertaleModLib.Util; 7 | 8 | EnsureDataLoaded(); 9 | 10 | string texFolder = PromptChooseDirectory(); 11 | if (texFolder is null) 12 | { 13 | return; 14 | } 15 | 16 | bool padded = ScriptQuestion("Export sprites with padding?"); 17 | 18 | List spritesToDump = new(); 19 | List splitStringsList = new(); 20 | 21 | string inputtedText = SimpleTextInput("Menu", "Enter the name of the sprites", "", true); 22 | string[] individualLineArray = inputtedText.Split('\n', StringSplitOptions.RemoveEmptyEntries); 23 | foreach (var oneLine in individualLineArray) 24 | { 25 | splitStringsList.Add(oneLine.Trim()); 26 | } 27 | foreach (string listElement in splitStringsList) 28 | { 29 | foreach (UndertaleSprite spr in Data.Sprites) 30 | { 31 | if (spr is null) 32 | { 33 | continue; 34 | } 35 | if (listElement.Equals(spr.Name.Content, StringComparison.InvariantCultureIgnoreCase)) 36 | { 37 | spritesToDump.Add(spr); 38 | } 39 | } 40 | } 41 | 42 | SetProgressBar(null, "Sprites", 0, spritesToDump.Count); 43 | StartProgressBarUpdater(); 44 | 45 | TextureWorker worker = null; 46 | using (worker = new()) 47 | { 48 | await Task.Run(() => 49 | { 50 | foreach (UndertaleSprite sprToDump in spritesToDump) 51 | { 52 | DumpSprite(sprToDump); 53 | } 54 | }); 55 | } 56 | await StopProgressBarUpdater(); 57 | HideProgressBar(); 58 | 59 | void DumpSprite(UndertaleSprite sprite) 60 | { 61 | for (int i = 0; i < sprite.Textures.Count; i++) 62 | { 63 | if (sprite.Textures[i]?.Texture is not null) 64 | { 65 | worker.ExportAsPNG(sprite.Textures[i].Texture, Path.Combine(texFolder, $"{sprite.Name.Content}_{i}.png"), null, padded); 66 | } 67 | } 68 | IncrementProgress(); 69 | } -------------------------------------------------------------------------------- /UndertaleModTool/Scripts/Resource Importers/ImportAllEmbeddedTextures.csx: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | using System; 3 | using System.IO; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | using UndertaleModLib.Util; 7 | 8 | EnsureDataLoaded(); 9 | 10 | // Setup root export folder. 11 | string embeddedTexturesPath = PromptChooseDirectory(); 12 | if (embeddedTexturesPath is null) 13 | { 14 | throw new ScriptException("The import folder was not set."); 15 | } 16 | 17 | string subPath = embeddedTexturesPath; 18 | int i = 0; 19 | foreach (UndertaleEmbeddedTexture target in Data.EmbeddedTextures) 20 | { 21 | if (target is null) 22 | { 23 | i++; 24 | continue; 25 | } 26 | string filename = $"{i}.png"; 27 | try 28 | { 29 | target.TextureData.Image = GMImage.FromPng(File.ReadAllBytes(Path.Combine(subPath, filename))) 30 | .ConvertToFormat(target.TextureData.Image.Format); 31 | } 32 | catch (Exception ex) 33 | { 34 | ScriptMessage($"Failed to import {filename}: {ex.Message}"); 35 | } 36 | i++; 37 | } 38 | 39 | ScriptMessage("Import complete."); 40 | -------------------------------------------------------------------------------- /UndertaleModTool/Scripts/Resource Importers/ImportAllStringsJSON.csx: -------------------------------------------------------------------------------- 1 | using System.Text.Json; 2 | 3 | EnsureDataLoaded(); 4 | 5 | string path = PromptLoadFile("", "JSON files (*.json)|*.json|TXT files (*.txt)|*.txt|All files (*.*)|*.*"); 6 | if (string.IsNullOrWhiteSpace(path)) 7 | { 8 | throw new ScriptException("The import file was not set."); 9 | } 10 | 11 | string file = File.ReadAllText(path); 12 | JsonElement json = JsonSerializer.Deserialize(file); 13 | JsonElement.ArrayEnumerator array = json.GetProperty("Strings").EnumerateArray(); 14 | int i = 0; 15 | foreach (JsonElement elmnt in array) 16 | Data.Strings[i++].Content = elmnt.ToString(); 17 | ScriptMessage("Successfully imported"); 18 | -------------------------------------------------------------------------------- /UndertaleModTool/Scripts/Resource Importers/ImportAllTilesets.csx: -------------------------------------------------------------------------------- 1 | // Adapted from original script by Grossley 2 | 3 | using System.Text; 4 | using System; 5 | using System.IO; 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | using UndertaleModLib.Util; 9 | using ImageMagick; 10 | 11 | EnsureDataLoaded(); 12 | 13 | // Setup root import folder. 14 | string subPath = PromptChooseDirectory(); 15 | if (subPath is null) 16 | { 17 | throw new ScriptException("The import folder was not set."); 18 | } 19 | 20 | SetProgressBar(null, "Tilesets", 0, Data.Backgrounds.Count); 21 | StartProgressBarUpdater(); 22 | 23 | await ImportTilesets(); 24 | 25 | await StopProgressBarUpdater(); 26 | HideProgressBar(); 27 | ScriptMessage("Import complete."); 28 | 29 | async Task ImportTilesets() 30 | { 31 | await Task.Run(() => Parallel.ForEach(Data.Backgrounds, ImportTileset)); 32 | } 33 | 34 | void ImportTileset(UndertaleBackground tileset) 35 | { 36 | if (tileset is not null) 37 | { 38 | string filename = $"{tileset.Name.Content}.png"; 39 | try 40 | { 41 | string path = Path.Combine(subPath, filename); 42 | if (File.Exists(path)) 43 | { 44 | using MagickImage img = TextureWorker.ReadBGRAImageFromFile(path); 45 | tileset.Texture.ReplaceTexture(img); 46 | } 47 | } 48 | catch (Exception ex) 49 | { 50 | ScriptMessage($"Failed to import {filename} (index {Data.Backgrounds.IndexOf(tileset)}): {ex.Message}"); 51 | } 52 | } 53 | 54 | IncrementProgress(); 55 | } 56 | -------------------------------------------------------------------------------- /UndertaleModTool/Scripts/Resource Importers/ImportAssembly.csx: -------------------------------------------------------------------------------- 1 | // Script by Jockeholm based off of a script by Kneesnap. 2 | // Major help and edited by Samuel Roy 3 | 4 | using System; 5 | using System.IO; 6 | using System.Threading.Tasks; 7 | using System.Linq; 8 | using UndertaleModLib.Util; 9 | 10 | EnsureDataLoaded(); 11 | 12 | // Check code directory. 13 | string importFolder = PromptChooseDirectory(); 14 | if (importFolder == null) 15 | throw new ScriptException("The import folder was not set."); 16 | 17 | string[] dirFiles = Directory.GetFiles(importFolder); 18 | if (dirFiles.Length == 0) 19 | throw new ScriptException("The selected folder is empty."); 20 | else if (!dirFiles.Any(x => x.EndsWith(".asm"))) 21 | throw new ScriptException("The selected folder doesn't contain any ASM file."); 22 | 23 | bool stopOnError = ScriptQuestion("Stop importing on error?"); 24 | 25 | SetProgressBar(null, "Files", 0, dirFiles.Length); 26 | StartProgressBarUpdater(); 27 | 28 | SyncBinding("Strings, Code, CodeLocals, Scripts, GlobalInitScripts, GameObjects, Functions, Variables", true); 29 | await Task.Run(() => 30 | { 31 | foreach (string file in dirFiles) 32 | { 33 | string asm = File.ReadAllText(file); 34 | string codeName = Path.GetFileNameWithoutExtension(file); 35 | 36 | if (Data.Code.ByName(codeName) is UndertaleCode code) 37 | { 38 | try 39 | { 40 | code.Replace(Assembler.Assemble(asm, Data)); 41 | } 42 | catch (Exception e) 43 | { 44 | if (stopOnError) 45 | { 46 | throw new ScriptException($"Error on code entry {codeName}:\n{e}"); 47 | } 48 | else 49 | { 50 | ScriptError($"Error on code entry {codeName}:\n{e}"); 51 | } 52 | } 53 | } 54 | else 55 | { 56 | if (stopOnError) 57 | { 58 | throw new ScriptException($"Missing code entry {codeName} (must exist before importing)"); 59 | } 60 | else 61 | { 62 | ScriptError($"Missing code entry {codeName} (must exist before importing)"); 63 | } 64 | } 65 | 66 | IncrementProgress(); 67 | } 68 | }); 69 | DisableAllSyncBindings(); 70 | 71 | await StopProgressBarUpdater(); 72 | HideProgressBar(); 73 | ScriptMessage("All files successfully imported."); -------------------------------------------------------------------------------- /UndertaleModTool/Scripts/Resource Importers/ImportGML.csx: -------------------------------------------------------------------------------- 1 | // Script by Jockeholm based off of a script by Kneesnap. 2 | // Major help and edited by Samuel Roy 3 | 4 | using System; 5 | using System.IO; 6 | using System.Threading.Tasks; 7 | using System.Linq; 8 | using UndertaleModLib.Util; 9 | 10 | EnsureDataLoaded(); 11 | 12 | // Check code directory. 13 | string importFolder = PromptChooseDirectory(); 14 | if (importFolder == null) 15 | throw new ScriptException("The import folder was not set."); 16 | 17 | string[] dirFiles = Directory.GetFiles(importFolder); 18 | if (dirFiles.Length == 0) 19 | throw new ScriptException("The selected folder is empty."); 20 | else if (!dirFiles.Any(x => x.EndsWith(".gml"))) 21 | throw new ScriptException("The selected folder doesn't contain any GML file."); 22 | 23 | // Ask whether they want to link code. If no, will only generate code entry. 24 | // If yes, will try to add code to objects and scripts depending upon its name 25 | bool doParse = ScriptQuestion("Do you want to automatically attempt to link imported code?"); 26 | 27 | SetProgressBar(null, "Files", 0, dirFiles.Length); 28 | StartProgressBarUpdater(); 29 | 30 | SyncBinding("Strings, Code, CodeLocals, Scripts, GlobalInitScripts, GameObjects, Functions, Variables", true); 31 | await Task.Run(() => 32 | { 33 | UndertaleModLib.Compiler.CodeImportGroup importGroup = new(Data); 34 | foreach (string file in dirFiles) 35 | { 36 | IncrementProgress(); 37 | 38 | string code = File.ReadAllText(file); 39 | string codeName = Path.GetFileNameWithoutExtension(file); 40 | importGroup.QueueReplace(codeName, code); 41 | } 42 | SetProgressBar(null, "Performing final import...", dirFiles.Length, dirFiles.Length); 43 | importGroup.Import(); 44 | }); 45 | DisableAllSyncBindings(); 46 | 47 | await StopProgressBarUpdater(); 48 | HideProgressBar(); 49 | ScriptMessage("All files successfully imported."); 50 | -------------------------------------------------------------------------------- /UndertaleModTool/Scripts/Sample Scripts/DeltaMILK.csx: -------------------------------------------------------------------------------- 1 | EnsureDataLoaded(); 2 | 3 | ScriptMessage("Select the MILK that you prefer\nReplace every non-background sprite with milk (for Deltarune)\nby krzys_h"); 4 | 5 | var milk = Data.Sprites.ByName("spr_checkers_milk").Textures[0].Texture; 6 | foreach (var sprite in Data.Sprites) 7 | { 8 | if (sprite is null) 9 | continue; 10 | if (sprite.Name.Content.StartsWith("bg_")) 11 | continue; 12 | foreach (var tex in sprite.Textures) 13 | tex.Texture = milk; 14 | } -------------------------------------------------------------------------------- /UndertaleModTool/Scripts/Sample Scripts/TheWholeWorldRevolving.csx: -------------------------------------------------------------------------------- 1 | EnsureDataLoaded(); 2 | 3 | if (Data?.GeneralInfo?.DisplayName?.Content.ToLower() == "deltarune chapter 1 & 2") 4 | { 5 | ScriptError("Error 0: Incompatible with the new Deltarune Chapter 1 & 2 demo"); 6 | return; 7 | } 8 | else if (Data?.GeneralInfo?.DisplayName?.Content.ToLower() == "deltarune chapter 1&2") 9 | { 10 | ScriptError("Error 1: Incompatible with the new Deltarune Chapter 1 & 2 demo"); 11 | return; 12 | } 13 | 14 | if (!Data.IsGameMaker2()) 15 | { 16 | ScriptMessage("This is not a GMS2 game."); 17 | return; 18 | } 19 | 20 | var gml_Object_obj_time_Step_1 = Data.GameObjects.ByName("obj_time").EventHandlerFor(EventType.Step, EventSubtypeStep.BeginStep, Data); 21 | 22 | UndertaleModLib.Compiler.CodeImportGroup importGroup = new(Data); 23 | importGroup.QueueAppend(gml_Object_obj_time_Step_1, @" 24 | __view_set(4, 0, ((__view_get(e__VW_append.Angle, 0) + (sin((global.time / 15)) / 2)) + 1)); 25 | __view_set(11, 0, (__view_get(e__VW_append.XPort, 0) + (cos((global.time / 10)) * 1.5))); 26 | __view_set(12, 0, (__view_get(e__VW_append.YPort, 0) + (sin((global.time / 10)) * 1.5))); 27 | 28 | enum e__VW_append 29 | { 30 | Angle = 4, 31 | XPort = 11, 32 | YPort = 12 33 | } 34 | "); 35 | importGroup.Import(); 36 | 37 | ChangeSelection(gml_Object_obj_time_Step_1); 38 | ScriptMessage("* The whole world is spinning, spinning\n\nJEVIL HARDMODE CHAOS LOADED\nI CAN DO ANYTHING\nby krzys_h, Kneesnap"); 39 | -------------------------------------------------------------------------------- /UndertaleModTool/Scripts/Technical Scripts/16_To_17.csx: -------------------------------------------------------------------------------- 1 | if (Data?.GeneralInfo.BytecodeVersion == 16) 2 | { 3 | if (!ScriptQuestion("Upgrade bytecode from 16 to 17?")) 4 | { 5 | ScriptMessage("Cancelled."); 6 | return; 7 | } 8 | Data.GeneralInfo.BytecodeVersion = 17; 9 | if (!Data.IsVersionAtLeast(2, 2, 2, 302)) 10 | Data.SetGMS2Version(2, 2, 2, 302); 11 | Data.FORM.Chunks["TGIN"] = new UndertaleChunkTGIN(); 12 | String[] order = {"GEN8", "OPTN", "LANG", "EXTN", "SOND", "AGRP", "SPRT", "BGND", "PATH", "SCPT", "GLOB", "SHDR", "FONT", "TMLN", "OBJT", "ROOM", "DAFL", "EMBI", "TPAG", "TGIN", "CODE", "VARI", "FUNC", "STRG", "TXTR", "AUDO"}; 13 | Dictionary newChunks = new Dictionary(); 14 | foreach (String name in order) 15 | newChunks[name] = Data.FORM.Chunks[name]; 16 | Data.FORM.Chunks = newChunks; 17 | UndertaleTextureGroupInfo tgin = new UndertaleTextureGroupInfo(); 18 | tgin.Name = Data.Strings.MakeString("Default"); 19 | for (var i = 0; i < Data.EmbeddedTextures.Count; i++) 20 | { 21 | tgin.TexturePages.Add(new UndertaleResourceById() { Resource = Data.EmbeddedTextures[i] }); 22 | } 23 | for (var i = 0; i < Data.Sprites.Count; i++) 24 | { 25 | tgin.Sprites.Add(new UndertaleResourceById() { Resource = Data.Sprites[i] }); 26 | } 27 | for (var i = 0; i < Data.Fonts.Count; i++) 28 | { 29 | tgin.Fonts.Add(new UndertaleResourceById() { Resource = Data.Fonts[i] }); 30 | } 31 | for (var i = 0; i < Data.Backgrounds.Count; i++) 32 | { 33 | tgin.Tilesets.Add(new UndertaleResourceById() { Resource = Data.Backgrounds[i] }); 34 | } 35 | Data.TextureGroupInfo.Add(tgin); 36 | ScriptMessage("Upgraded from 16 to 17 successfully. This game can be run on any runner newer than GMS 2.2.2.302 but older than GMS 2.3. Save the game to apply the changes."); 37 | } 38 | -------------------------------------------------------------------------------- /UndertaleModTool/Scripts/Technical Scripts/ConvertFrom17to16.csx: -------------------------------------------------------------------------------- 1 | //For use playing the Switch version of Deltarune on PC with the regular Deltarune runner - by Grossley 2 | 3 | EnsureDataLoaded(); 4 | 5 | if (Data.IsVersionAtLeast(2, 3)) 6 | { 7 | bool x = RunUMTScript(Path.Combine(ExePath, "Scripts", "Technical Scripts", "ConvertFrom17to16_for_2.3.csx")); 8 | if (x == false) 9 | ScriptError("ConvertFrom17to16_for_2.3.csx failed!"); 10 | return; 11 | } 12 | 13 | if (Data?.GeneralInfo.BytecodeVersion >= 17) 14 | { 15 | if (!ScriptQuestion("Downgrade bytecode from 17 to 16?")) 16 | { 17 | ScriptMessage("Cancelled."); 18 | return; 19 | } 20 | Data.GeneralInfo.BytecodeVersion = 16; 21 | if (Data.FORM.Chunks.ContainsKey("TGIN")) 22 | Data.FORM.Chunks.Remove("TGIN"); 23 | Data.SetGMS2Version(2); 24 | ScriptMessage("Downgraded from 17 to 16 successfully. Save the game to apply the changes."); 25 | } 26 | else 27 | { 28 | ScriptError("This is already bytecode 16 or lower.", "Error"); 29 | return; 30 | } -------------------------------------------------------------------------------- /UndertaleModTool/Scripts/Technical Scripts/ExportAssetOrder.csx: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | using System; 3 | using System.IO; 4 | 5 | EnsureDataLoaded(); 6 | 7 | string outputPath = PromptSaveFile(".txt", "TXT files (*.txt)|*.txt|All files (*.*)|*.*"); 8 | if (string.IsNullOrWhiteSpace(outputPath)) 9 | { 10 | return; 11 | } 12 | 13 | void WriteAssetNames(StreamWriter writer, IList assets) where T : UndertaleNamedResource 14 | { 15 | if (assets.Count == 0) 16 | return; 17 | foreach (var asset in assets) 18 | { 19 | if (asset is not null) 20 | writer.WriteLine(asset.Name?.Content ?? assets.IndexOf(asset).ToString()); 21 | else 22 | writer.WriteLine("(null)"); 23 | } 24 | } 25 | 26 | using (StreamWriter writer = new StreamWriter(outputPath)) 27 | { 28 | // Write Sounds. 29 | writer.WriteLine("@@sounds@@"); 30 | WriteAssetNames(writer, Data.Sounds); 31 | 32 | // Write Sprites. 33 | writer.WriteLine("@@sprites@@"); 34 | WriteAssetNames(writer, Data.Sprites); 35 | 36 | // Write Backgrounds. 37 | writer.WriteLine("@@backgrounds@@"); 38 | WriteAssetNames(writer, Data.Backgrounds); 39 | 40 | // Write Paths. 41 | writer.WriteLine("@@paths@@"); 42 | WriteAssetNames(writer, Data.Paths); 43 | 44 | // Write Scripts. 45 | writer.WriteLine("@@scripts@@"); 46 | WriteAssetNames(writer, Data.Scripts); 47 | 48 | // Write Fonts. 49 | writer.WriteLine("@@fonts@@"); 50 | WriteAssetNames(writer, Data.Fonts); 51 | 52 | // Write Objects. 53 | writer.WriteLine("@@objects@@"); 54 | WriteAssetNames(writer, Data.GameObjects); 55 | 56 | // Write Timelines. 57 | writer.WriteLine("@@timelines@@"); 58 | WriteAssetNames(writer, Data.Timelines); 59 | 60 | // Write Rooms. 61 | writer.WriteLine("@@rooms@@"); 62 | WriteAssetNames(writer, Data.Rooms); 63 | 64 | // Write Shaders. 65 | writer.WriteLine("@@shaders@@"); 66 | WriteAssetNames(writer, Data.Shaders); 67 | 68 | // Write Extensions. 69 | writer.WriteLine("@@extensions@@"); 70 | WriteAssetNames(writer, Data.Extensions); 71 | 72 | // TODO: Perhaps detect GMS2.3, export those asset names as well. 73 | } 74 | -------------------------------------------------------------------------------- /UndertaleModTool/Scripts/Technical Scripts/GetAllChunkNames.csx: -------------------------------------------------------------------------------- 1 | EnsureDataLoaded(); 2 | 3 | string x = "String[] order = {\""; 4 | foreach (string key in Data.FORM.Chunks.Keys) 5 | { 6 | if (key != "AUDO") 7 | x += (key + "\", \""); 8 | else 9 | x += (key + "\"};"); 10 | } 11 | return x; 12 | -------------------------------------------------------------------------------- /UndertaleModTool/Scripts/Technical Scripts/GetTempDirectory.csx: -------------------------------------------------------------------------------- 1 | // For debugging NET bundled versions 2 | return System.AppDomain.CurrentDomain.BaseDirectory; -------------------------------------------------------------------------------- /UndertaleModTool/Scripts/Technical Scripts/MatchRoomOrderToInternalOrder.csx: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Collections; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using UndertaleModLib.Util; 8 | 9 | EnsureDataLoaded(); 10 | 11 | string res = ""; 12 | string x = ""; 13 | List currentList = new List(); 14 | 15 | for (int i = 0; i < Data.GeneralInfo.RoomOrder.Count; i++) 16 | { 17 | x = Data.GeneralInfo.RoomOrder[i].ToString(); 18 | string stringBeforeChar = x.Substring(0, x.IndexOf(" ")); 19 | res += (stringBeforeChar + "\n"); 20 | currentList.Add(stringBeforeChar); 21 | } 22 | Reorganize(Data.Rooms, currentList); 23 | return res; 24 | 25 | void Reorganize(IList list, List order) where T : UndertaleNamedResource, new() 26 | { 27 | Dictionary temp = new Dictionary(); 28 | for (int i = 0; i < list.Count; i++) 29 | { 30 | T asset = list[i]; 31 | string assetName = asset.Name?.Content; 32 | if (order.Contains(assetName)) 33 | { 34 | temp[assetName] = asset; 35 | } 36 | } 37 | 38 | List addOrder = new List(); 39 | for (int i = order.Count - 1; i >= 0; i--) 40 | { 41 | T asset; 42 | try 43 | { 44 | asset = temp[order[i]]; 45 | } catch (Exception e) 46 | { 47 | throw new ScriptException("Missing asset with name \"" + order[i] + "\""); 48 | } 49 | addOrder.Add(asset); 50 | } 51 | 52 | foreach (T asset in addOrder) 53 | list.Remove(asset); 54 | foreach (T asset in addOrder) 55 | list.Insert(0, asset); 56 | } 57 | -------------------------------------------------------------------------------- /UndertaleModTool/Scripts/Technical Scripts/RealignRoomInternalOrder.csx: -------------------------------------------------------------------------------- 1 | EnsureDataLoaded(); 2 | 3 | Data.GeneralInfo.RoomOrder.Clear(); 4 | for (int i = 0; i < Data.Rooms.Count; i++) 5 | { 6 | Data.GeneralInfo.RoomOrder.Add(new UndertaleResourceById() { Resource = Data.Rooms[i] }); 7 | } 8 | -------------------------------------------------------------------------------- /UndertaleModTool/Scripts/Technical Scripts/RestoreMissingCodeLocals.csx: -------------------------------------------------------------------------------- 1 | EnsureDataLoaded(); 2 | if (Data.CodeLocals is null) 3 | { 4 | ScriptMessage("Cannot run on this game, bytecode >= 15 and GM < 2024.8 required!"); 5 | return; 6 | } 7 | int newCount = 0; 8 | foreach (UndertaleCode code in Data.Code) 9 | { 10 | if (code is null) 11 | continue; 12 | if (Data.CodeLocals.ByName(code.Name.Content) == null) 13 | { 14 | UndertaleCodeLocals locals = new UndertaleCodeLocals(); 15 | locals.Name = code.Name; 16 | UndertaleCodeLocals.LocalVar argsLocal = new UndertaleCodeLocals.LocalVar(); 17 | argsLocal.Name = Data.Strings.MakeString("arguments"); 18 | argsLocal.Index = 0; 19 | locals.Locals.Add(argsLocal); 20 | code.LocalsCount = 1; 21 | Data.CodeLocals.Add(locals); 22 | newCount += 1; 23 | } 24 | } 25 | ScriptMessage("Added code locals for " + newCount.ToString() + " codes successfully"); 26 | return; 27 | -------------------------------------------------------------------------------- /UndertaleModTool/Scripts/Technical Scripts/SpriteOriginCopy.csx: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | using System.IO; 4 | using System.Threading; 5 | using UndertaleModLib; 6 | using UndertaleModLib.Scripting; 7 | using UndertaleModLib.Models; 8 | 9 | EnsureDataLoaded(); 10 | 11 | ScriptMessage("Select the file to copy the sprite origins from"); 12 | 13 | UndertaleData donorData; 14 | string donorDataPath = PromptLoadFile(null, null); 15 | if (donorDataPath == null) 16 | throw new ScriptException("The donor data path was not set."); 17 | 18 | using (var stream = new FileStream(donorDataPath, FileMode.Open, FileAccess.Read)) 19 | donorData = UndertaleIO.Read(stream, (warning, _) => ScriptMessage("A warning occured while trying to load " + donorDataPath + ":\n" + warning)); 20 | 21 | foreach (var sprite in Data.Sprites) 22 | { 23 | if (sprite is null) 24 | continue; 25 | var donorSpr = donorData.Sprites.ByName(sprite.Name.Content); 26 | if (donorSpr is not null) 27 | { 28 | sprite.OriginXWrapper = donorSpr.OriginXWrapper; 29 | sprite.OriginYWrapper = donorSpr.OriginYWrapper; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /UndertaleModTool/Scripts/Technical Scripts/ToggleAlignment.csx: -------------------------------------------------------------------------------- 1 | // TPAG 4 byte alignment toggler by Grossley 2 | EnsureDataLoaded(); 3 | 4 | if (ScriptQuestion("TPAG 4-Byte alignment is currently: " + (Data.IsTPAG4ByteAligned ? "enabled" : "disabled") + ". Would you like to change it?")) 5 | Data.IsTPAG4ByteAligned = ScriptQuestion("Toggle TPAG 4-byte alignment by Grossley\n\nYes = Enable TPAG 4-byte alignment (Android/Vita)\nNo = Disable TPAG 4-byte alignment"); 6 | else 7 | { 8 | ScriptMessage("Change cancelled."); 9 | return; 10 | } 11 | ScriptMessage("TPAG 4-Byte alignment is now: " + (Data.IsTPAG4ByteAligned ? "enabled" : "disabled") + "."); -------------------------------------------------------------------------------- /UndertaleModTool/Scripts/Technical Scripts/VariableFixer.csx: -------------------------------------------------------------------------------- 1 | // Made by Creepersbane 2 | 3 | EnsureDataLoaded(); 4 | 5 | ScriptMessage(@"This script will most likely fix data files with the following symptoms: 6 | 7 | If, after loading the data file after code editing, any of the following occur: 8 | 9 | No window appears at all 10 | ""Out of memory!"" error appears while loading 11 | No "".gamelog.txt"" is produced 12 | The "".gamelog.txt"" is produced but appears to fail after Steam initialization. 13 | The game window appears but is frozen indefinitely 14 | "); 15 | Data.GeneralInfo.IsDebuggerDisabled = true; 16 | int globalNum = 0; 17 | int selfNum = 0; 18 | foreach(var vari in Data.Variables) 19 | { 20 | if (vari is null) 21 | continue; 22 | if (vari.InstanceType == UndertaleInstruction.InstanceType.Global) 23 | { 24 | vari.VarID = globalNum++; 25 | } 26 | else if ((vari.InstanceType == UndertaleInstruction.InstanceType.Self) && (vari.VarID >= 0)) 27 | { 28 | vari.VarID = selfNum++; 29 | } 30 | } 31 | Data.VarCount1 = (uint)globalNum; 32 | Data.VarCount2 = (uint)selfNum; 33 | 34 | ScriptMessage(@"Complete, please save and run the game now to apply and test your changes."); -------------------------------------------------------------------------------- /UndertaleModTool/Scripts/UTDR Scripts/Borders/bg_border_anime.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnderminersTeam/UndertaleModTool/7051c3ac7fa739fe688be4ed7d466bf63050d5b3/UndertaleModTool/Scripts/UTDR Scripts/Borders/bg_border_anime.png -------------------------------------------------------------------------------- /UndertaleModTool/Scripts/UTDR Scripts/Borders/bg_border_casino.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnderminersTeam/UndertaleModTool/7051c3ac7fa739fe688be4ed7d466bf63050d5b3/UndertaleModTool/Scripts/UTDR Scripts/Borders/bg_border_casino.png -------------------------------------------------------------------------------- /UndertaleModTool/Scripts/UTDR Scripts/Borders/bg_border_castle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnderminersTeam/UndertaleModTool/7051c3ac7fa739fe688be4ed7d466bf63050d5b3/UndertaleModTool/Scripts/UTDR Scripts/Borders/bg_border_castle.png -------------------------------------------------------------------------------- /UndertaleModTool/Scripts/UTDR Scripts/Borders/bg_border_dog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnderminersTeam/UndertaleModTool/7051c3ac7fa739fe688be4ed7d466bf63050d5b3/UndertaleModTool/Scripts/UTDR Scripts/Borders/bg_border_dog.png -------------------------------------------------------------------------------- /UndertaleModTool/Scripts/UTDR Scripts/Borders/bg_border_fire.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnderminersTeam/UndertaleModTool/7051c3ac7fa739fe688be4ed7d466bf63050d5b3/UndertaleModTool/Scripts/UTDR Scripts/Borders/bg_border_fire.png -------------------------------------------------------------------------------- /UndertaleModTool/Scripts/UTDR Scripts/Borders/bg_border_line.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnderminersTeam/UndertaleModTool/7051c3ac7fa739fe688be4ed7d466bf63050d5b3/UndertaleModTool/Scripts/UTDR Scripts/Borders/bg_border_line.png -------------------------------------------------------------------------------- /UndertaleModTool/Scripts/UTDR Scripts/Borders/bg_border_line_1080.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnderminersTeam/UndertaleModTool/7051c3ac7fa739fe688be4ed7d466bf63050d5b3/UndertaleModTool/Scripts/UTDR Scripts/Borders/bg_border_line_1080.png -------------------------------------------------------------------------------- /UndertaleModTool/Scripts/UTDR Scripts/Borders/bg_border_rad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnderminersTeam/UndertaleModTool/7051c3ac7fa739fe688be4ed7d466bf63050d5b3/UndertaleModTool/Scripts/UTDR Scripts/Borders/bg_border_rad.png -------------------------------------------------------------------------------- /UndertaleModTool/Scripts/UTDR Scripts/Borders/bg_border_ruins.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnderminersTeam/UndertaleModTool/7051c3ac7fa739fe688be4ed7d466bf63050d5b3/UndertaleModTool/Scripts/UTDR Scripts/Borders/bg_border_ruins.png -------------------------------------------------------------------------------- /UndertaleModTool/Scripts/UTDR Scripts/Borders/bg_border_sepia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnderminersTeam/UndertaleModTool/7051c3ac7fa739fe688be4ed7d466bf63050d5b3/UndertaleModTool/Scripts/UTDR Scripts/Borders/bg_border_sepia.png -------------------------------------------------------------------------------- /UndertaleModTool/Scripts/UTDR Scripts/Borders/bg_border_truelab.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnderminersTeam/UndertaleModTool/7051c3ac7fa739fe688be4ed7d466bf63050d5b3/UndertaleModTool/Scripts/UTDR Scripts/Borders/bg_border_truelab.png -------------------------------------------------------------------------------- /UndertaleModTool/Scripts/UTDR Scripts/Borders/bg_border_tundra.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnderminersTeam/UndertaleModTool/7051c3ac7fa739fe688be4ed7d466bf63050d5b3/UndertaleModTool/Scripts/UTDR Scripts/Borders/bg_border_tundra.png -------------------------------------------------------------------------------- /UndertaleModTool/Scripts/UTDR Scripts/Borders/bg_border_water1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnderminersTeam/UndertaleModTool/7051c3ac7fa739fe688be4ed7d466bf63050d5b3/UndertaleModTool/Scripts/UTDR Scripts/Borders/bg_border_water1.png -------------------------------------------------------------------------------- /UndertaleModTool/Scripts/UTDR Scripts/Borders/border_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnderminersTeam/UndertaleModTool/7051c3ac7fa739fe688be4ed7d466bf63050d5b3/UndertaleModTool/Scripts/UTDR Scripts/Borders/border_dark.png -------------------------------------------------------------------------------- /UndertaleModTool/Scripts/UTDR Scripts/Borders/border_dark_ch1_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnderminersTeam/UndertaleModTool/7051c3ac7fa739fe688be4ed7d466bf63050d5b3/UndertaleModTool/Scripts/UTDR Scripts/Borders/border_dark_ch1_0.png -------------------------------------------------------------------------------- /UndertaleModTool/Scripts/UTDR Scripts/Borders/border_dw_castletown_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnderminersTeam/UndertaleModTool/7051c3ac7fa739fe688be4ed7d466bf63050d5b3/UndertaleModTool/Scripts/UTDR Scripts/Borders/border_dw_castletown_0.png -------------------------------------------------------------------------------- /UndertaleModTool/Scripts/UTDR Scripts/Borders/border_dw_city_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnderminersTeam/UndertaleModTool/7051c3ac7fa739fe688be4ed7d466bf63050d5b3/UndertaleModTool/Scripts/UTDR Scripts/Borders/border_dw_city_0.png -------------------------------------------------------------------------------- /UndertaleModTool/Scripts/UTDR Scripts/Borders/border_dw_cyber_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnderminersTeam/UndertaleModTool/7051c3ac7fa739fe688be4ed7d466bf63050d5b3/UndertaleModTool/Scripts/UTDR Scripts/Borders/border_dw_cyber_0.png -------------------------------------------------------------------------------- /UndertaleModTool/Scripts/UTDR Scripts/Borders/border_dw_mansion_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnderminersTeam/UndertaleModTool/7051c3ac7fa739fe688be4ed7d466bf63050d5b3/UndertaleModTool/Scripts/UTDR Scripts/Borders/border_dw_mansion_0.png -------------------------------------------------------------------------------- /UndertaleModTool/Scripts/UTDR Scripts/Borders/border_light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnderminersTeam/UndertaleModTool/7051c3ac7fa739fe688be4ed7d466bf63050d5b3/UndertaleModTool/Scripts/UTDR Scripts/Borders/border_light.png -------------------------------------------------------------------------------- /UndertaleModTool/Scripts/UTDR Scripts/Borders/border_light_ch1_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnderminersTeam/UndertaleModTool/7051c3ac7fa739fe688be4ed7d466bf63050d5b3/UndertaleModTool/Scripts/UTDR Scripts/Borders/border_light_ch1_0.png -------------------------------------------------------------------------------- /UndertaleModTool/Scripts/UTDR Scripts/Borders/border_line_1080_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnderminersTeam/UndertaleModTool/7051c3ac7fa739fe688be4ed7d466bf63050d5b3/UndertaleModTool/Scripts/UTDR Scripts/Borders/border_line_1080_0.png -------------------------------------------------------------------------------- /UndertaleModTool/Scripts/UTDR Scripts/Borders/border_lw_town_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnderminersTeam/UndertaleModTool/7051c3ac7fa739fe688be4ed7d466bf63050d5b3/UndertaleModTool/Scripts/UTDR Scripts/Borders/border_lw_town_0.png -------------------------------------------------------------------------------- /UndertaleModTool/Scripts/UTDR Scripts/DeltaruneReloadJSON.csx: -------------------------------------------------------------------------------- 1 | EnsureDataLoaded(); 2 | 3 | if (Data?.GeneralInfo?.DisplayName?.Content.ToLower() == "deltarune chapter 1 & 2") 4 | { 5 | ScriptError("Error 0: Incompatible with the new Deltarune Chapter 1 & 2 demo"); 6 | return; 7 | } 8 | else if (Data?.GeneralInfo?.DisplayName?.Content.ToLower() == "deltarune chapter 1&2") 9 | { 10 | ScriptError("Error 1: Incompatible with the new Deltarune Chapter 1 & 2 demo"); 11 | return; 12 | } 13 | 14 | ScriptMessage("Press F12 to reload the current JSON"); 15 | 16 | var code = Data.GameObjects.ByName("obj_time").EventHandlerFor(EventType.KeyPress, EventSubtypeKey.vk_f12, Data); 17 | 18 | UndertaleModLib.Compiler.CodeImportGroup importGroup = new(Data); 19 | importGroup.QueueReplace(code, "scr_84_lang_load()"); 20 | importGroup.Import(); 21 | 22 | ChangeSelection(code); 23 | ScriptMessage("Patched!"); -------------------------------------------------------------------------------- /UndertaleModTool/Scripts/UTDR Scripts/DeltaruneTTFFonts.csx: -------------------------------------------------------------------------------- 1 | EnsureDataLoaded(); 2 | 3 | if (Data?.GeneralInfo?.DisplayName?.Content.ToLower() == "deltarune chapter 1 & 2") 4 | { 5 | ScriptError("Error 0: Incompatible with the new Deltarune Chapter 1 & 2 demo"); 6 | return; 7 | } 8 | else if (Data?.GeneralInfo?.DisplayName?.Content.ToLower() == "deltarune chapter 1&2") 9 | { 10 | ScriptError("Error 1: Incompatible with the new Deltarune Chapter 1 & 2 demo"); 11 | return; 12 | } 13 | 14 | // Remove all current fonts 15 | // This is necessary because I need to add fonts under IDs that are normally used by these resources 16 | // TODO: Japanese fonts won't work at all because I didn't add support for that 17 | Data.Fonts.Clear(); 18 | 19 | var obj_time = Data.GameObjects.ByName("obj_time"); 20 | 21 | Data.Functions.EnsureDefined("font_add", Data.Strings); 22 | 23 | UndertaleModLib.Compiler.CodeImportGroup importGroup = new(Data); 24 | importGroup.QueueAppend(obj_time.EventHandlerFor(EventType.Create, Data), @" 25 | // NOTE: According to GMS documentation the font ranges are ignored with ttf fonts, and that seems to be indeed the case 26 | font_add(""wingding.ttf"", 8, false, false, 32, 127); 27 | font_add(""8bitoperator_jve.ttf"", 10, false, false, 32, 127); 28 | font_add(""8bitoperator_jve.ttf"", 8, false, false, 32, 127); 29 | font_add(""CryptOfTomorrow.ttf"", 6, false, false, 32, 127); 30 | font_add(""DotumChe.ttf"", 8, true, false, 32, 127); 31 | font_add(""DotumChe.ttf"", 12, true, false, 32, 127); 32 | font_add(""hachicro.ttf"", 10, true, false, 32, 127); 33 | font_add(""Mars Needs Cunnilingus.ttf"", 9, false, false, 32, 127); 34 | font_add(""comic.ttf"", 10, true, false, 32, 127); 35 | font_add(""PAPYRUS.TTF"", 8, true, false, 32, 127); 36 | "); 37 | importGroup.Import(); 38 | 39 | ChangeSelection(obj_time); 40 | -------------------------------------------------------------------------------- /UndertaleModTool/Scripts/UTDR Scripts/DisableDogcheck.csx: -------------------------------------------------------------------------------- 1 | // Disables Dogcheck for all Undertale and Deltarune versions. 2 | // Made by Grossley. 3 | 4 | EnsureDataLoaded(); 5 | 6 | if (Data?.GeneralInfo?.DisplayName?.Content.ToLower() == "deltarune chapter 1 & 2") 7 | { 8 | ScriptError("Error 0: Incompatible with the new Deltarune Chapter 1 & 2 demo"); 9 | return; 10 | } 11 | else if (Data?.GeneralInfo?.DisplayName?.Content.ToLower() == "deltarune chapter 1&2") 12 | { 13 | ScriptError("Error 1: Incompatible with the new Deltarune Chapter 1 & 2 demo"); 14 | return; 15 | } 16 | 17 | 18 | ScriptMessage(@"This script disables Dogcheck for 19 | all Undertale and Deltarune versions."); 20 | 21 | UndertaleModLib.Compiler.CodeImportGroup importGroup = new(Data) 22 | { 23 | ThrowOnNoOpFindReplace = true 24 | }; 25 | 26 | // Removes the invoking of the dog check script and the actual check itself from "gml_Script_scr_load". 27 | UndertaleCode scr_load = Data.Scripts.ByName("scr_load", true)?.Code; 28 | if (Data.GeneralInfo.Name.Content == "NXTALE" || Data.GeneralInfo.Name.Content.StartsWith("UNDERTALE")) 29 | { 30 | importGroup.QueueFindReplace(scr_load, 31 | """ 32 | scr_dogcheck(); 33 | if (dogcheck == 1) 34 | """, 35 | ""); 36 | } 37 | else if (Data.GeneralInfo.DisplayName.Content == "SURVEY_PROGRAM" || Data.GeneralInfo.DisplayName.Content == "DELTARUNE Chapter 1") 38 | { 39 | importGroup.QueueFindReplace(scr_load, "scr_dogcheck()", "0"); 40 | } 41 | else 42 | { 43 | ScriptError("This script can only be used with\nUndertale or Deltarune Chapter 1.", "Not Undertale or Deltarune"); 44 | return; 45 | } 46 | 47 | importGroup.Import(); 48 | 49 | // Done. 50 | ChangeSelection(scr_load); // Show. 51 | ScriptMessage(@"Dogcheck is now disabled."); 52 | -------------------------------------------------------------------------------- /UndertaleModTool/Scripts/UTDR Scripts/ShowRoomName.csx: -------------------------------------------------------------------------------- 1 | // Adds room ID and name display under the debug mode timer display 2 | EnsureDataLoaded(); 3 | ScriptMessage("Show room name and ID in debug mode\nby krzys_h, Kneesnap\n"); 4 | Data.Functions.EnsureDefined("room_get_name", Data.Strings); // required for Deltarune 5 | string displayName = Data?.GeneralInfo?.DisplayName?.Content.ToLower(); 6 | bool isDeltarune = displayName.StartsWith("deltarune chapter"); 7 | 8 | UndertaleModLib.Compiler.CodeImportGroup importGroup = new(Data); 9 | var gml_Object_obj_time_Draw_64 = Data.GameObjects.ByName("obj_time").EventHandlerFor(EventType.Draw, EventSubtypeDraw.DrawGUI, Data); 10 | if (isDeltarune) 11 | { 12 | importGroup.QueueAppend(gml_Object_obj_time_Draw_64, @" 13 | if (scr_debug()) 14 | { 15 | draw_set_color(c_yellow); 16 | draw_text(10, 30, room); 17 | draw_text(50, 30, room_get_name(room)); 18 | }"); 19 | } 20 | else 21 | { 22 | importGroup.QueueAppend(gml_Object_obj_time_Draw_64, @" 23 | if (global.debug) 24 | { 25 | draw_set_color(c_yellow); 26 | draw_text(10, 30, room); 27 | draw_text(50, 30, room_get_name(room)); 28 | }"); 29 | } 30 | importGroup.Import(); 31 | 32 | ChangeSelection(gml_Object_obj_time_Draw_64); 33 | ScriptMessage("Patched!"); 34 | -------------------------------------------------------------------------------- /UndertaleModTool/Scripts/UTDR Scripts/TouchControls_data/controls.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnderminersTeam/UndertaleModTool/7051c3ac7fa739fe688be4ed7d466bf63050d5b3/UndertaleModTool/Scripts/UTDR Scripts/TouchControls_data/controls.png -------------------------------------------------------------------------------- /UndertaleModTool/Scripts/UTDR Scripts/TouchControls_data/gml_Object_obj_mobilecontrols_Create_0.gml: -------------------------------------------------------------------------------- 1 | settings_font = {_font} 2 | settings_num_x = {_settingsnumx} 3 | zx = 510 4 | zy = 340 5 | xx = 560 6 | xy = 280 7 | cx = 610 8 | cy = 220 9 | button_scale = 2.5 10 | analog_scale = 3.3 11 | analog_posx = -42 12 | analog_posy = 232.5 13 | analog_edit_selected = 0 14 | analog_center_x = (analog_posx + (((59 * analog_scale) / 2) - ((41 * analog_scale) / 2))) 15 | analog_center_y = (analog_posy + (((59 * analog_scale) / 2) - ((41 * analog_scale) / 2))) 16 | arrowkeys_area_size = 19.675 17 | arrowkeys_back_area_size = 45 18 | joystick_type = 0 19 | settingsx = 5 20 | settingsy = 5 21 | edit = 0 22 | black_fade = 0 23 | text_black_fade = 0 24 | controls_opacity = 0.5 25 | if file_exists("touchconfig.ini") 26 | { 27 | ini_open("touchconfig.ini") 28 | zx = ini_read_real("CONFIG", "zx", zx) 29 | zy = ini_read_real("CONFIG", "zy", zy) 30 | xx = ini_read_real("CONFIG", "xx", xx) 31 | xy = ini_read_real("CONFIG", "xy", xy) 32 | cx = ini_read_real("CONFIG", "cx", cx) 33 | cy = ini_read_real("CONFIG", "cy", cy) 34 | analog_posx = ini_read_real("CONFIG", "analog_posx", analog_posx) 35 | analog_posy = ini_read_real("CONFIG", "analog_posy", analog_posy) 36 | button_scale = ini_read_real("CONFIG", "button_scale", button_scale) 37 | analog_scale = ini_read_real("CONFIG", "analog_scale", analog_scale) 38 | joystick_type = ini_read_real("CONFIG", "joystick_type", joystick_type) 39 | controls_opacity = ini_read_real("CONFIG", "controls_opacity", controls_opacity) 40 | ini_close() 41 | } 42 | virtual_key_zp = virtual_key_add(0, 0, 0, 0, 1) 43 | virtual_key_xp = virtual_key_add(0, 0, 0, 0, 2) 44 | virtual_key_cp = virtual_key_add(0, 0, 0, 0, 3) 45 | virtual_key_analogp = virtual_key_add(0, 0, 0, 0, 4) 46 | virtual_key_settings = virtual_key_add(0, 0, 0, 0, 5) 47 | virtual_key_z = virtual_key_add(0, 0, 0, 0, 6) 48 | virtual_key_x = virtual_key_add(0, 0, 0, 0, 7) 49 | virtual_key_c = virtual_key_add(0, 0, 0, 0, 8) 50 | virtual_key_up = virtual_key_add(0, 0, 0, 0, 9) 51 | virtual_key_right = virtual_key_add(0, 0, 0, 0, 10) 52 | virtual_key_left = virtual_key_add(0, 0, 0, 0, 11) 53 | virtual_key_down = virtual_key_add(0, 0, 0, 0, 12) 54 | virtual_key_analog = virtual_key_add(0, 0, 0, 0, 13) 55 | latestGuiH = display_get_gui_height() 56 | latestGuiW = display_get_gui_width() -------------------------------------------------------------------------------- /UndertaleModTool/Scripts/UTDR Scripts/UndertaleDebugMsg.csx: -------------------------------------------------------------------------------- 1 | EnsureDataLoaded(); 2 | ScriptMessage("DebugMsg - Displays dialogue messages\nwhile debug mode is enabled.\n\nAuthor: krzys-h, Kneesnap"); 3 | 4 | var code = Data.GameObjects.ByName("obj_time").EventHandlerFor(EventType.Draw, EventSubtypeDraw.DrawGUI, Data); 5 | 6 | UndertaleModLib.Compiler.CodeImportGroup importGroup = new(Data); 7 | importGroup.QueueAppend(code, @" 8 | if global.debug 9 | { 10 | draw_set_color(c_white); 11 | var drawYPosition = 0 12 | for (var i = 0; i < array_length_1d(global.msg); i++) 13 | { 14 | var currentMessage = global.msg[i] 15 | if ((!(currentMessage == ""%%%"")) && (!(currentMessage == ""%%"")) && (!(currentMessage == ""%""))) 16 | { 17 | drawYPosition++ 18 | draw_text(10, (drawYPosition * 15 + 50), currentMessage) 19 | } 20 | } 21 | }"); 22 | importGroup.Import(); 23 | 24 | ChangeSelection(code); 25 | ScriptMessage("DebugMsg - Finished."); -------------------------------------------------------------------------------- /UndertaleModTool/Scripts/UTDR Scripts/UndertaleDebugToggler.csx: -------------------------------------------------------------------------------- 1 | // Makes it possible to switch debug mode on and off using F1 2 | 3 | EnsureDataLoaded(); 4 | 5 | string internalName = Data.GeneralInfo.Name.Content; 6 | string displayName = Data.GeneralInfo.DisplayName.Content; 7 | if (internalName != "NXTALE" && !internalName.StartsWith("UNDERTALE")) 8 | { 9 | ScriptError("Unsupported game version."); 10 | return; 11 | } 12 | 13 | UndertaleModLib.Compiler.CodeImportGroup importGroup = new(Data); 14 | 15 | var code = Data.GameObjects.ByName("obj_time").EventHandlerFor(EventType.KeyPress, EventSubtypeKey.vk_f1, Data); 16 | importGroup.QueueReplace(code, 17 | """ 18 | if (global.debug) 19 | { 20 | global.debug = 0; 21 | } 22 | else 23 | { 24 | global.debug = 1; 25 | } 26 | """); 27 | 28 | importGroup.Import(); 29 | 30 | ChangeSelection(code); 31 | ScriptMessage("F1 will now toggle debug mode."); 32 | 33 | -------------------------------------------------------------------------------- /UndertaleModTool/Scripts/UTDR Scripts/UndertaleFixAlphysLabCrashAndroid.csx: -------------------------------------------------------------------------------- 1 | // Made by GitMuslim 2 | 3 | using System; 4 | using System.IO; 5 | using System.Threading.Tasks; 6 | using System.Linq; 7 | using UndertaleModLib.Util; 8 | 9 | EnsureDataLoaded(); 10 | 11 | if (Data.GeneralInfo?.DisplayName?.Content.ToLower() != "undertale") 12 | { 13 | if (!ScriptQuestion("This game isn't Undertale, thus the script may not work properly. Continue anyway?")) 14 | { 15 | return; 16 | } 17 | } 18 | 19 | UndertaleModLib.Compiler.CodeImportGroup importGroup = new(Data); 20 | importGroup.QueueReplace("gml_Object_obj_labdarkness_Create_0", ""); 21 | importGroup.QueueReplace("gml_Object_obj_labdarkness_Draw_0", ""); 22 | importGroup.QueueFindReplace("gml_Object_obj_labcamera_Create_0", "if (global.osflavor == 2)", "if (global.osflavor == 4)"); 23 | importGroup.Import(); -------------------------------------------------------------------------------- /UndertaleModTool/Scripts/UTDR Scripts/UndertaleGoToRoom.csx: -------------------------------------------------------------------------------- 1 | // Replaces the debug mode "Create system_information_962" option with "Go to room" 2 | 3 | EnsureDataLoaded(); 4 | 5 | ScriptMessage("Add 'Go to room' dialog under F3\nby krzys-h, Kneesnap"); 6 | 7 | var code = Data.GameObjects.ByName("obj_time").EventHandlerFor(EventType.KeyPress, EventSubtypeKey.vk_f3, Data); 8 | 9 | Data.Functions.EnsureDefined("get_integer", Data.Strings); 10 | 11 | UndertaleModLib.Compiler.CodeImportGroup importGroup = new(Data); 12 | importGroup.QueueReplace(code, @" 13 | if (global.debug) 14 | room_goto(get_integer(""Go to room"", room)); 15 | "); 16 | importGroup.Import(); 17 | 18 | ChangeSelection(code); 19 | ScriptMessage("Patched!"); -------------------------------------------------------------------------------- /UndertaleModTool/Scripts/UTDR Scripts/UndertaleRunButton.csx: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | using System; 3 | using System.IO; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | using System.Text.RegularExpressions; 7 | 8 | EnsureDataLoaded(); 9 | 10 | if (Data?.GeneralInfo?.DisplayName?.Content.ToLower() == "deltarune chapter 1 & 2") 11 | { 12 | ScriptError("Error 0: Incompatible with the new Deltarune Chapter 1 & 2 demo"); 13 | return; 14 | } 15 | else if (Data?.GeneralInfo?.DisplayName?.Content.ToLower() == "deltarune chapter 1&2") 16 | { 17 | ScriptError("Error 1: Incompatible with the new Deltarune Chapter 1 & 2 demo"); 18 | return; 19 | } 20 | 21 | ScriptMessage("Add a run button to Undertale (Backspace)"); 22 | 23 | UndertaleModLib.Compiler.CodeImportGroup importGroup = new(Data) 24 | { 25 | ThrowOnNoOpFindReplace = true 26 | }; 27 | importGroup.QueueFindReplace("gml_Object_obj_mainchara_Step_0", "(global.debug == 1)", "(true)"); 28 | importGroup.Import(); 29 | 30 | ScriptMessage("Run button in Undertale enabled!"); -------------------------------------------------------------------------------- /UndertaleModTool/Scripts/UTDR Scripts/UndertaleSimplifyBattlegroupScript.csx: -------------------------------------------------------------------------------- 1 | EnsureDataLoaded(); 2 | 3 | if (Data?.GeneralInfo?.DisplayName?.Content.ToLower() == "deltarune chapter 1 & 2") 4 | { 5 | ScriptError("Error 0: Incompatible with the new Deltarune Chapter 1 & 2 demo"); 6 | return; 7 | } 8 | else if (Data?.GeneralInfo?.DisplayName?.Content.ToLower() == "deltarune chapter 1&2") 9 | { 10 | ScriptError("Error 1: Incompatible with the new Deltarune Chapter 1 & 2 demo"); 11 | return; 12 | } 13 | 14 | UndertaleModLib.Compiler.CodeImportGroup importGroup = new(Data) 15 | { 16 | ThrowOnNoOpFindReplace = true 17 | }; 18 | importGroup.QueueFindReplace("gml_Script_scr_battlegroup", "global.monstertype[0] = 47", ""); 19 | importGroup.QueueFindReplace("gml_Script_scr_battlegroup", "global.monstertype[1] = 0", ""); 20 | importGroup.QueueFindReplace("gml_Script_scr_battlegroup", "global.monstertype[2] = 0", ""); 21 | importGroup.QueueFindReplace("gml_Script_scr_battlegroup", @"global.batmusic = caster_load(""music/battle1.ogg"")", ""); 22 | importGroup.QueueFindReplace("gml_Script_scr_battlegroup", "global.actfirst = 0", ""); 23 | importGroup.QueueFindReplace("gml_Script_scr_battlegroup", "global.extraintro = 0", ""); 24 | importGroup.QueueFindReplace("gml_Script_scr_battlegroup", "global.battlelv = 0", ""); 25 | importGroup.QueueFindReplace("gml_Script_scr_battlegroup", "global.msc = 0", ""); 26 | importGroup.QueuePrepend("gml_Script_scr_battlegroup", 27 | @" 28 | global.monstertype[0] = 47 29 | global.monstertype[1] = 0 30 | global.monstertype[2] = 0 31 | global.batmusic = caster_load(""music/battle1.ogg"") 32 | global.actfirst = 0 33 | global.extraintro = 0 34 | global.battlelv = 0 35 | global.msc = 0 36 | "); 37 | importGroup.Import(); -------------------------------------------------------------------------------- /UndertaleModTool/Scripts/UTDR Scripts/UndertaleTTFFonts.csx: -------------------------------------------------------------------------------- 1 | EnsureDataLoaded(); 2 | 3 | var displayName = Data.GeneralInfo?.DisplayName?.Content.ToLower(); 4 | if (!(displayName == "undertale" || displayName == "nxtale")) 5 | { 6 | ScriptError("Error 1: This script only works with Undertale!"); 7 | } 8 | 9 | // Remove all current fonts 10 | // This is necessary because I need to add fonts under IDs that are normally used by these resources 11 | // TODO: Japanese fonts won't work at all because I didn't add support for that 12 | Data.Fonts.Clear(); 13 | 14 | Data.Functions.EnsureDefined("font_add", Data.Strings); // Allow font_add. 15 | 16 | var obj_time_Create_0 = Data.GameObjects.ByName("obj_time").EventHandlerFor(EventType.Create, Data); 17 | 18 | UndertaleModLib.Compiler.CodeImportGroup importGroup = new(Data); 19 | importGroup.QueueAppend(obj_time_Create_0, @" 20 | // NOTE: According to GMS documentation the font ranges are ignored with ttf fonts, and that seems to be indeed the case 21 | font_add(""wingding.ttf"", 12, false, false, 32, 127); 22 | font_add(""8bitoperator_jve.ttf"", 24, false, false, 32, 127); 23 | font_add(""8bitoperator_jve.ttf"", 12, false, false, 32, 127); 24 | font_add(""CryptOfTomorrow.ttf"", 6, false, false, 32, 127); 25 | font_add(""DotumChe.ttf"", 12, true, false, 32, 127); 26 | font_add(""DotumChe.ttf"", 48, true, false, 32, 127); 27 | font_add(""hachicro.ttf"", 24, true, false, 32, 127); 28 | font_add(""Mars Needs Cunnilingus.ttf"", 18, false, false, 32, 127); 29 | font_add(""comic.ttf"", 10, true, false, 32, 127); 30 | font_add(""PAPYRUS.TTF"", 8, true, false, 32, 127); 31 | "); 32 | importGroup.Import(); 33 | 34 | ScriptMessage("Successfully externalized fonts for Undertale!"); 35 | -------------------------------------------------------------------------------- /UndertaleModTool/Scripts/UTDR Scripts/UndertaleWASD.csx: -------------------------------------------------------------------------------- 1 | // Enables WASD controls for all Undertale versions 2 | // Made by Grossley with invaluable help from Lil Alien. 3 | 4 | EnsureDataLoaded(); 5 | 6 | ScriptMessage(@"This script enables WASD controls for 7 | all Undertale versions."); 8 | 9 | if (Data.GeneralInfo.Name.Content == "NXTALE" || Data.GeneralInfo.Name.Content.StartsWith("UNDERTALE")) 10 | { 11 | UndertaleModLib.Compiler.CodeImportGroup importGroup = new(Data); 12 | importGroup.QueueAppend(Data.Code.ByName("gml_Object_obj_time_Step_1"), @" 13 | if (global.debug == 0) 14 | { 15 | if keyboard_check(ord(""W"")) 16 | keyboard_key_press(vk_up) 17 | if keyboard_check_released(ord(""W"")) 18 | keyboard_key_release(vk_up) 19 | if keyboard_check(ord(""A"")) 20 | keyboard_key_press(vk_left) 21 | if keyboard_check_released(ord(""A"")) 22 | keyboard_key_release(vk_left) 23 | if keyboard_check(ord(""S"")) 24 | keyboard_key_press(vk_down) 25 | if keyboard_check_released(ord(""S"")) 26 | keyboard_key_release(vk_down) 27 | if keyboard_check(ord(""D"")) 28 | keyboard_key_press(vk_right) 29 | if keyboard_check_released(ord(""D"")) 30 | keyboard_key_release(vk_right) 31 | } 32 | "); 33 | importGroup.Import(); 34 | } 35 | else 36 | { 37 | ScriptError("This script can only be used with Undertale!", "Not Undertale"); 38 | return; 39 | } 40 | 41 | // Done. 42 | ChangeSelection(Data.Code.ByName("gml_Object_obj_time_Step_1")); // Show. 43 | ScriptMessage("WASD is now enabled."); -------------------------------------------------------------------------------- /UndertaleModTool/Scripts/Utility Scripts/FindAndReplace.csx: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | using System; 3 | using System.IO; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | using System.Text.RegularExpressions; 7 | using System.Linq; 8 | 9 | EnsureDataLoaded(); 10 | 11 | if (!ScriptQuestion("This will make changes across all of the code! Are you sure you'd like to continue?")) 12 | { 13 | return; 14 | } 15 | bool caseSensitive = ScriptQuestion("Case sensitive?"); 16 | bool multiline = ScriptQuestion("Multi-line search?"); 17 | bool isRegex = ScriptQuestion("Is regex search?"); 18 | String keyword = SimpleTextInput("Enter search terms", "Search box below", "", multiline); 19 | String replacement = SimpleTextInput("Enter replacement term", "Search box below", "", multiline); 20 | 21 | List toDump = Data.Code.Where(c => c.ParentEntry is null).ToList(); 22 | 23 | SetProgressBar(null, "Code Entries", 0, toDump.Count); 24 | StartProgressBarUpdater(); 25 | 26 | SyncBinding("Strings, Variables, Functions", true); 27 | await Task.Run(() => 28 | { 29 | UndertaleModLib.Compiler.CodeImportGroup importGroup = new(Data, null, Data.ToolInfo.DecompilerSettings); 30 | foreach (UndertaleCode code in toDump) 31 | { 32 | if (code is not null) 33 | { 34 | if (isRegex) 35 | { 36 | importGroup.QueueRegexFindReplace(code, keyword, replacement, caseSensitive); 37 | } 38 | else 39 | { 40 | importGroup.QueueFindReplace(code, keyword, replacement, caseSensitive); 41 | } 42 | } 43 | IncrementProgress(); 44 | } 45 | SetProgressBar(null, "Final code import...", toDump.Count, toDump.Count); 46 | importGroup.Import(); 47 | }); 48 | DisableAllSyncBindings(); 49 | 50 | await StopProgressBarUpdater(); 51 | HideProgressBar(); 52 | ScriptMessage("Completed"); 53 | -------------------------------------------------------------------------------- /UndertaleModTool/Scripts/Utility Scripts/FontEditor_README.md: -------------------------------------------------------------------------------- 1 | # UndertaleFontEditor 2 | UndertaleModTool script, font editor GUI inspired by Lazy Shell 3 | 4 | (can export and import to ZIP) 5 | 6 | 7 | -------------------------------------------------------------------------------- /UndertaleModTool/Scripts/Utility Scripts/GoToRoom_AutoLocatePersistentObj.csx: -------------------------------------------------------------------------------- 1 | // Replaces the debug mode "Create system_information_962" option with "Go to room" 2 | 3 | EnsureDataLoaded(); 4 | 5 | ScriptMessage("Add 'Go to room' dialog under F3\nby krzys-h, Kneesnap"); 6 | 7 | bool pers = false; 8 | UndertaleGameObject obj_pers = null; 9 | foreach (UndertaleGameObject obj in Data.GameObjects) 10 | { 11 | if (obj is null) 12 | continue; 13 | if (obj.Persistent) 14 | { 15 | pers = true; 16 | obj_pers = obj; 17 | break; 18 | } 19 | } 20 | if (!pers) 21 | { 22 | ScriptMessage("Impossible to run, no persistent object!"); 23 | return; 24 | } 25 | 26 | var code = obj_pers.EventHandlerFor(EventType.KeyPress, EventSubtypeKey.vk_f3, Data); 27 | 28 | Data.Functions.EnsureDefined("get_integer", Data.Strings); 29 | 30 | UndertaleModLib.Compiler.CodeImportGroup importGroup = new(Data); 31 | importGroup.QueueReplace(code, @" 32 | room_goto(get_integer(""Go to room"", room)); 33 | "); 34 | importGroup.Import(); 35 | 36 | ChangeSelection(code); 37 | ScriptMessage("Patched!"); -------------------------------------------------------------------------------- /UndertaleModTool/Scripts/Utility Scripts/MergeImages.csx: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Collections; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using UndertaleModLib.Util; 8 | using ImageMagick; 9 | 10 | string importFolderA = PromptChooseDirectory(); 11 | if (importFolderA is null) 12 | throw new ScriptException("The import folder was not set."); 13 | 14 | string importFolderB = PromptChooseDirectory(); 15 | if (importFolderB is null) 16 | throw new ScriptException("The import folder was not set."); 17 | 18 | string exportFolder = PromptChooseDirectory(); 19 | if (exportFolder is null) 20 | throw new ScriptException("The export folder was not set."); 21 | 22 | // Loop over all PNG files in folder A 23 | DirectoryInfo textureDirectoryA = new DirectoryInfo(importFolderA); 24 | FileInfo[] filesA = textureDirectoryA.GetFiles("*.png", SearchOption.AllDirectories); 25 | foreach (FileInfo fileA in filesA) 26 | { 27 | // If there's no matching file found, abort 28 | if (!File.Exists(Path.Combine(importFolderB, fileA.Name))) 29 | continue; 30 | 31 | // Load both images, and calculate dimensions of resulting image 32 | using MagickImage imageA = TextureWorker.ReadBGRAImageFromFile(Path.Combine(importFolderA, fileA.Name)); 33 | using MagickImage imageB = TextureWorker.ReadBGRAImageFromFile(Path.Combine(importFolderB, fileA.Name)); 34 | uint width = imageA.Width + imageB.Width; 35 | uint height = Math.Max(imageA.Height, imageB.Height); 36 | 37 | // Make combined image, and composite both images onto it 38 | using MagickImage outputImage = new(MagickColor.FromRgba(0, 0, 0, 0), width, height); 39 | outputImage.Composite(imageA, 0, 0, CompositeOperator.Copy); 40 | outputImage.Composite(imageB, (int)imageA.Width, 0, CompositeOperator.Copy); 41 | 42 | // Save image to output folder 43 | TextureWorker.SaveImageToFile(outputImage, Path.Combine(exportFolder, fileA.Name)); 44 | } 45 | -------------------------------------------------------------------------------- /UndertaleModTool/Windows/DebugDataDialog.xaml: -------------------------------------------------------------------------------- 1 |  10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | The debugger is enabled. Do you want to generate debugger data? 23 | Decompiled 24 | Decompile whole source code when saving for display in the debugger 25 | NOTE: no breakpoint support yet! Except for the script entry point 26 | Disassembly, partial breakpoints 27 | Full disassembly, but you can set breakpoints only on B, BR, BT, POP, POPZ, RET and EXIT. This is still a lot more than that poor GM:S debugger was designed to handle, so allow up to 5 minutes for it to load the data file (yes, seriously) 28 | Disassembly, full breakpoints 29 | Gave up after like 15 minutes of waiting, don't even try this 30 | No debug data 31 | 32 | 33 | -------------------------------------------------------------------------------- /UndertaleModTool/Windows/DebugDataDialog.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Windows; 7 | using System.Windows.Controls; 8 | using System.Windows.Data; 9 | using System.Windows.Documents; 10 | using System.Windows.Input; 11 | using System.Windows.Media; 12 | using System.Windows.Media.Imaging; 13 | using System.Windows.Shapes; 14 | 15 | namespace UndertaleModTool 16 | { 17 | /// 18 | /// Logika interakcji dla klasy DebugDataDialog.xaml 19 | /// 20 | public partial class DebugDataDialog : Window 21 | { 22 | public enum DebugDataMode 23 | { 24 | FullAssembler, 25 | PartialAssembler, 26 | Decompiled, 27 | NoDebug 28 | } 29 | 30 | public DebugDataMode Result { get; private set; } = DebugDataMode.NoDebug; 31 | 32 | public DebugDataDialog() 33 | { 34 | InitializeComponent(); 35 | } 36 | private void Window_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e) 37 | { 38 | if (!IsVisible || IsLoaded) 39 | return; 40 | 41 | if (Settings.Instance.EnableDarkMode) 42 | MainWindow.SetDarkTitleBarForWindow(this, true, false); 43 | } 44 | 45 | private void Button1_Click(object sender, RoutedEventArgs e) 46 | { 47 | Result = DebugDataMode.Decompiled; 48 | Close(); 49 | } 50 | 51 | private void Button2_Click(object sender, RoutedEventArgs e) 52 | { 53 | Result = DebugDataMode.PartialAssembler; 54 | Close(); 55 | } 56 | 57 | private void Button3_Click(object sender, RoutedEventArgs e) 58 | { 59 | Result = DebugDataMode.FullAssembler; 60 | Close(); 61 | } 62 | 63 | private void Button4_Click(object sender, RoutedEventArgs e) 64 | { 65 | Result = DebugDataMode.NoDebug; 66 | Close(); 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /UndertaleModTool/Windows/LoaderDialog.xaml: -------------------------------------------------------------------------------- 1 |  10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /UndertaleModTool/Windows/RuntimePicker.xaml: -------------------------------------------------------------------------------- 1 |  10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /UndertaleModTool/Windows/StringUpdateWindow.xaml: -------------------------------------------------------------------------------- 1 |  10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | You edited a string reference! What do you want to do? 18 | Change only this one value 19 | Change all occurrences of this value 20 | Abort the change 21 | 22 | 23 | -------------------------------------------------------------------------------- /UndertaleModTool/Windows/StringUpdateWindow.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Windows; 7 | using System.Windows.Controls; 8 | using System.Windows.Data; 9 | using System.Windows.Documents; 10 | using System.Windows.Input; 11 | using System.Windows.Media; 12 | using System.Windows.Media.Imaging; 13 | using System.Windows.Shapes; 14 | 15 | namespace UndertaleModTool 16 | { 17 | /// 18 | /// Logika interakcji dla klasy StringUpdateWindow.xaml 19 | /// 20 | public partial class StringUpdateWindow : Window 21 | { 22 | public enum ResultType 23 | { 24 | Cancel = 0, 25 | ChangeOneValue, 26 | ChangeReferencedValue 27 | } 28 | 29 | public ResultType Result { get; private set; } = ResultType.Cancel; 30 | 31 | public StringUpdateWindow() 32 | { 33 | InitializeComponent(); 34 | } 35 | private void Window_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e) 36 | { 37 | if (!IsVisible || IsLoaded) 38 | return; 39 | 40 | if (Settings.Instance.EnableDarkMode) 41 | MainWindow.SetDarkTitleBarForWindow(this, true, false); 42 | } 43 | 44 | private void Button1_Click(object sender, RoutedEventArgs e) 45 | { 46 | Result = ResultType.ChangeOneValue; 47 | Close(); 48 | } 49 | 50 | private void Button2_Click(object sender, RoutedEventArgs e) 51 | { 52 | Result = ResultType.ChangeReferencedValue; 53 | Close(); 54 | } 55 | 56 | private void Button3_Click(object sender, RoutedEventArgs e) 57 | { 58 | Result = ResultType.Cancel; 59 | Close(); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /UndertaleModTool/Windows/TextInputDialog.xaml: -------------------------------------------------------------------------------- 1 |  10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /UndertaleModTool/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnderminersTeam/UndertaleModTool/7051c3ac7fa739fe688be4ed7d466bf63050d5b3/UndertaleModTool/icon.ico -------------------------------------------------------------------------------- /UndertaleModToolUpdater/UndertaleModToolUpdater.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | net6.0-windows 6 | publish\ 7 | true 8 | win-x64;win-x86 9 | True 10 | en 11 | en 12 | en 13 | 14 | 1591 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /images/flowey.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnderminersTeam/UndertaleModTool/7051c3ac7fa739fe688be4ed7d466bf63050d5b3/images/flowey.gif -------------------------------------------------------------------------------- /images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnderminersTeam/UndertaleModTool/7051c3ac7fa739fe688be4ed7d466bf63050d5b3/images/logo.png -------------------------------------------------------------------------------- /images/ribbit-dr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnderminersTeam/UndertaleModTool/7051c3ac7fa739fe688be4ed7d466bf63050d5b3/images/ribbit-dr.png --------------------------------------------------------------------------------