├── .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 |
31 |
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 |
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