Embeddable Hex Editor + Buffer Editor Plugin

Recently, I needed a way to edit buffers, but I couldn’t find any, so I decided to make a hex editor.

Features

  • Simple to embed. It is one script which you can copy and paste into your project. There are also two optional components: a data inspector and sample form provider.
  • Undo/redo.
  • Customizable. There are a few options to change the theming, layout, and hotkeys of the editor.

At the moment, there is no mobile support.

This is the default appearance:

Here is me embedding it into DataDelve so you can edit buffers:

Press for videos of it

Filling 10mb with hello world

Plugin embedding example


note: the messed up ascii and utf-8 has been fixed in the latest release

The Buffer Editor Plugin

I also made a buffer editor plugin which embeds this hex editor:

To use it run this in the command bar or a script: _G.editBuffer(buffer). This function will return a new buffer with the edited contents. At the moment hotkeys do not work with the plugin.

In cases where yielding is not desirable, you can also use _G.watchBuffer(id: string, buffer: buffer, refreshDelay: number?) which will open up a read-only version of the hex editor. refreshDelay is how often the editor will update the view to buffer’s current contents. The default is 0.25. The ID is so you can re-use a widget instead of having to open up a bunch for each buffer.

Press for video of plugin

How to Embed

First get the model (https://create.roblox.com/store/asset/84746640953606/hex-editor) or copy and paste the scripts from GitHub (GitHub - pinehappi/roblox-hex-editor: Embeddable hex editor for Roblox.).

Here is an example of how to embed the with no configuration:

local UserInputService = game:GetService("UserInputService")
local HexEditor = require(script.Parent.HexEditor)
local DataInspector = require(script.Parent.DataInspector)
local SampleFormProvider = require(script.Parent.SampleFormProvider)

local editor = HexEditor.new(
	buffer.fromstring("Hello, world!"),

	-- Options
	{
		container = script.Parent.Frame,
		sizing = "fillContainerY",
	},

	-- Dependencies
	{
		-- This can also be a GuiObject if you are embedding into a plugin widget.
		inputReceiver = UserInputService,

		-- This dependency is optional if you want the right click menu and everything that comes with it.
		forms = SampleFormProvider.generate(script.Parent.Frame, UserInputService)
	}
)

local inspector = DataInspector.new(
	editor,
	{
		container = script.Parent.Frame,
		sizing = "fillContainerY",
	}
)

Here is an example with full configuration:

local UserInputService = game:GetService("UserInputService")
local HexEditor = require(script.Parent.HexEditor)
local DataInspector = require(script.Parent.DataInspector)
local SampleFormProvider = require(script.Parent.SampleFormProvider)

local editor = HexEditor.new(
	buffer.fromstring("Hello, world!"),

	-- Second parameter is options.
    {
		container = script.Parent.Frame,
		sizing = "fillContainerY",
        theme = {
            cell = {
                background = Color3.fromRGB(27, 26, 26),
                text = Color3.fromRGB(204, 202, 194),

                hover = Color3.fromRGB(50, 43, 43),
                hoverText = Color3.fromRGB(204, 204, 194),

                select = Color3.fromRGB(25, 42, 60),
                selectText = Color3.fromRGB(204, 204, 194),

                cursorActive = Color3.fromRGB(20, 68, 100),
                cursorActiveText = Color3.fromRGB(204, 202, 194),

                cursor = Color3.fromRGB(46, 150, 225),
            },
            modifiedCell = {
                background = Color3.fromRGB(63, 44, 22),
                text = Color3.fromRGB(229, 227, 217),

                hover = Color3.fromRGB(93, 66, 34),
                hoverText = Color3.fromRGB(229, 227, 217),

                select = Color3.fromRGB(81, 75, 35),
                selectText = Color3.fromRGB(229, 227, 217),

                cursorActive = Color3.fromRGB(107, 59, 33),
                cursorActiveText = Color3.fromRGB(229, 227, 217),

                cursor = Color3.fromRGB(239, 119, 39),
            },
            addCell = {
                background = Color3.fromRGB(58, 68, 58),
                text = Color3.fromRGB(204, 202, 194),

                hover = Color3.fromRGB(97, 121, 92),
                hoverText = Color3.fromRGB(204, 204, 194),

                select = Color3.fromRGB(33, 50, 39),
                selectText = Color3.fromRGB(204, 204, 194),

                cursorActive = Color3.fromRGB(64, 100, 66),
                cursorActiveText = Color3.fromRGB(204, 202, 194),

                cursor = Color3.fromRGB(110, 209, 121),
            },

            headerText = Color3.fromRGB(120, 120, 130),
            headerActiveText = Color3.fromRGB(180, 180, 180),
            headerBackground = Color3.fromRGB(27, 26, 26),

            gutterText = Color3.fromRGB(120, 120, 130),
            gutterActiveText = Color3.fromRGB(180, 180, 180),
            gutterBackground = Color3.fromRGB(27, 26, 26),

            font = Font.fromEnum(Enum.Font.Code),
            textSize = 16,

            sectionGap = 12,
            cellPadding = 4,
            halfCellPadding = 0,

            scrollingFrameProperties = {
                ScrollBarThickness = 8,
                ScrollBarImageColor3 = Color3.new(0.5, 0.5, 0.5),
            }
        },
        layout = {
            columns = 16,
            showHeader = true,
            showGutter = true,
            showDecodedText = true,
            gutterColumns = 6,
        },
        hotkeys = {
            {
                action = "undo",
                keyCode = Enum.KeyCode.Z,
                modifierKeys = {Enum.ModifierKey.Ctrl},
            },
            {
                action = "redo",
                keyCode = Enum.KeyCode.Y,
                modifierKeys = {Enum.ModifierKey.Ctrl},
            },
            {
                action = "redo",
                keyCode = Enum.KeyCode.Z,
                modifierKeys = {Enum.ModifierKey.Ctrl, Enum.ModifierKey.Shift},
            },
            {
                action = "cycleCursorMode",
                keyCode = Enum.KeyCode.Insert,
            },
            {
                action = "selectAll",
                keyCode = Enum.KeyCode.A,
                modifierKeys = {Enum.ModifierKey.Ctrl},
            }
        }
	},

	-- Third parameter is dependencies.
	{
		-- This can also be a GuiObject if you are embedding into a plugin widget.
		inputReceiver = UserInputService,

		-- This forms dependency is optional if you want right click menu, fill selection,
		-- and insert null bytes operations.
		forms = SampleFormProvider.generate(script.Parent.Frame, UserInputService)
	}
)

local inspector = DataInspector.new(
	editor,
	{
		container = script.Parent.Frame,
		sizing = "fillContainerY",
        theme = {
            background = Color3.fromRGB(27, 26, 26),
            bodyPadding = 4,
            editorMargin = 12,

            headerBackground = Color3.fromRGB(27, 26, 26),
            headerText = Color3.fromRGB(204, 202, 194),

            keyText = Color3.fromRGB(204, 202, 194),
            valueText = Color3.fromRGB(204, 202, 194),
            invalidValueText = Color3.fromRGB(120, 120, 120),
            keyValuePadding = 24,
            rowPadding = 4,

            font = Font.fromEnum(Enum.Font.Code),
            textSize = 16,

            scrollingFrameProperties = {
                ScrollBarThickness = 8,
                ScrollBarImageColor3 = Color3.new(0.5, 0.5, 0.5),
            }
        }
	}
)
The Three Components Explained
  • HexEditor: This is the hex editor. It includes the decoded text section.
  • DataInspector: The DataInspector is an extra element that you can attach to the right side of the HexEditor. It will tell you about the data where the cursor of the hex editor is at.
  • SampleFormProvider: This is a module is used to generate the extra GUI elements/forms that the hex editor needs (mainly the right click menu). You can also make your own form provider to fit your UI since this is one is just a sample and is pretty ugly. If you don’t create your hex editor with the forms dependency, you don’t need this.
Controls
  • ↑↓←→/hjkl - Move cursor
  • S, ^, $, G - Move to start of buffer, start of line, end of line, end of buffer
  • [Insert key] or click the cursor - Cycle between replace and insert mode
  • Ctrl+Z, Ctrl+Y - Undo, redo

The hotkeys are configurable.

More Documentation

Coming soon (if I feel like it). If you are really confused just message me.

Links

Hex Editor GitHub: GitHub - pinehappi/roblox-hex-editor: Embeddable hex editor for Roblox.
Hex Editor Roblox Model: https://create.roblox.com/store/asset/84746640953606/hex-editor
Hex Editor Demo Place: hex editor demo - Roblox (the source for the plugin is in the ServerStorage of this)

Buffer Editor Plugin: https://create.roblox.com/store/asset/85182131613691/simple-hex-editor

Credits

15 Likes