Skip to content

Instantly share code, notes, and snippets.

@break-core
Last active July 8, 2025 13:42
Show Gist options
  • Select an option

  • Save break-core/5735f0d74ce244dd7755b58a6cefcd0b to your computer and use it in GitHub Desktop.

Select an option

Save break-core/5735f0d74ce244dd7755b58a6cefcd0b to your computer and use it in GitHub Desktop.
Documentation of the VisualSource V3/V4 format for RetroStudio

Preface

VisualSource is an encoded and compressed source code format which takes information of a script and turns it into condensed executable information.

This documentation documents how VisualSource 3.0 works in detail.

VisualSource V4 Changes

The encryption and data format is pretty much the same. However, the version was bumped to 0000000000000004 and a change was made to add extra data for blocks attached as an else block. It's stored in a new Property that comes after ChildBlocks called ElseChildBlock. What is stored is average block data (see Block data structure for information on that) and if one is not stored, the value stored is nil.

Data concepts

VisualSource uses 3 different data concepts within it's code:

  • Objects πŸ“¦
  • Properties ✏
  • List properties πŸ“„

Data structure

Objects start with 2 0x26 characters and following that is the name of the object.

Properties are structured as such:

0x26PROPNAME0x27PROPVAL

List properties are structured as such:

0x26PROPNAME0x27PROPVAL0x27PROPVAL

The "0x27PROPVAL" part is continued until the list ends

These all build up the core structure of how the final source is constructed.

Final structure (ordered based on position in the data)

Editor data structure

  • Editor πŸ“¦ - The start of the Editor object, essentially the root of everything.
    • CameraPosition ✏ - The XY position where the camera was at on the grid last time the script was saved.
    • CameraZoom ✏ - The zoom level of the camera last time the script was saved.

Block data structure

  • Block πŸ“¦ - The core object of a block. Whenever a new block is serialized, it's added after the previous one every time.
    • Type ✏ - The type of the block (aka the internal name, like SetVariable1)

    • Name ✏ - The name of the block (the visual name in the editor)

    • VisualPosition ✏ - The XY position of the block on the visual grid

    • ChildBlocks πŸ“„ - A list of block names that are connected to the block from first to last.

    • Inputs πŸ“„ - A list of inputs used in the block. Values are repeated until completion. It is stored as such:

      • The input name
      • The value type that was intended (eg. 2 for variable)
      • The value put in
      • The type of the value itself (if not a variable, eg. Bool)
    • Outputs πŸ“„ - A list of outputs used in the block. It is stored as such:

      • The name of the output
      • The value of the output
    • This process continues until all outputs have been logged.

Once EVERY block is completed, there is one final set of objects:

  • Comment πŸ“¦ - A comment (not the actual comment block itself) which wraps around under other blocks
    • Position ✏ - The position of the comment on the grid
    • Size ✏ - The size of the comment
    • Color ✏ - The color of the comment
    • Name ✏ - The name of the comment (aka the title)

But before we get into the main process, let's go over how each data type is serialized. There are many different types, that are serialized so let's go over each:

String

Strings are created if the value is a:

  • String
  • Choice (eg. an option from a dropdown in the editor)
  • Object (object chosen from the game)
  • Table (a reference to a table/table itself as a string)
  • Function (the function name as a string)
  • EventConnection (unsure what this is, probably a connection name)
  • Equation (the exact equation from using Solve Equation and stuff) They are typically represented as a string conversion of their value.

Number

Numbers are created if the value is explicitly a number/can be one.

Some prerequisites:

  • Number will be 0 if for whatever reason the value put in is bad.
  • Negative numbers are formatted too
  • Accounts for decimal numbers too including negative decimal numbers.

The result is that the main number and decimal number are converted to hex, and the decimal point is rounded to the nearest thousandth.

NOTE: FROM "BrickColor" TO "UDim2", THESE TYPES ARE ENCODED WITH HEXADECIMAL, SAME AS NUMBER)

THE PROPER WAY TO ENCODE A NUMBER AS A HEXADECIMAL IS WITH string.format("%X") IN LUAU)

Bool

Booleans are formatted as either 1 or 0 (1 being true, 0 being false)

BrickColor

BrickColors use the number of the brick color for formatting.

Color3

Color3 is converted from Color3 to RGB directly, separated by commas (eg. FF,FF,FF)

Vector3

The XYZ components are extracted and separated by commas (eg. 1.5,2,-3)

Vector2

Same thing as Vector3 but extracts the XY components (eg. 1.5,1.5)

UDim2

The X and Y Scale/Offset values are extracted and separated by commas (eg. 0.5,1A4,0.5,1A4)

Tuple

All tuple values are extracted and concatenated as a string separated by commas (same way as Color3/Vector3)

Encryption

VisualSource is decently encrypted, but luckily it's using open-source algorithms, so decrypting/encrypting is not hard.

To decrypt VisualSource:

  • Copy the ENTIRE data after οΏ½0000000000000003οΏ½ (the header)
  • First, use a Base93 decoder and run it on that.
  • After, run LibDeflate's deflation decoder on the value and you will get the VisualSource back!

To encrypt VisualSource:

  • Take your decoded VisualSource information (MAKE SURE IT IS ENCODED PROPERLY AND DOESNT INCLUDE THE HEADER)
  • Run LibDeflate's deflation encoder using the following settings:
{
  level = 6,
  strategy = "dynamic
}
  • Encode the value again with Base93
  • Append the header at the start, now you have real VisualSource!
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment