ReDem by ZugWare

Created by James L. Jendro

ReDEM was a third‑party terrain import tool created for RailWorks (later known as Train Simulator Classic) that gave route builders far more control than the game’s built‑in DEM system. Instead of relying on RailWorks’ limited heightmap importer, ReDEM allowed creators to load multiple DEM formats, stitch tiles, correct projections, and generate smooth, accurate terrain across large areas. For many route developers, it became an essential companion to RailWorks because it produced cleaner, more realistic landscapes and dramatically reduced the manual work required to shape terrain inside the editor.

forum profile https://www.trainsim.com/forums/member/57080-deanville

e-mail: deanville@attbi.com

Archived product page ReDem

ReDem installer https://www.digital-rails.com/files/redem_104b.zip

How to use ReDem described in a video tutorial Train Simulator 2016 – Route Building – #12 Real World Terrain

Collection of route building tutorial videos Steam Community :: Guide :: Route Building Tutorials – Train Simulator 2016

ReDem key generation code, program https://www.digital-rails.com/files/ReDem_Key.exe

public static bool CheckXP()
{
    int num;
    char[] chArray = Regex.Replace(ConfigOpt.GetOption("User").Trim() + "ReDem", @"\s+", " ").ToCharArray();
    int upperBound = chArray.GetUpperBound(0);
    byte[] buffer = new byte[upperBound + 1];
    int num3 = upperBound;
    for (num = 0; num <= num3; num++)
    {
        buffer[num] = (byte) Strings.Asc(chArray[num]);
    }
    byte[] buffer2 = new SHA512Managed().ComputeHash(buffer);
    string str2 = "";
    num = 0;
    do
    {
        str2 = str2 + Conversion.Hex(buffer2[num]);
        num++;
    }
    while (num <= 15);
    string str = "";
    string str3 = "ACEG8KM9QSUWY246";
    string str4 = "BDFHJLNPRTVXZ357";
    num = 0;
    do
    {
        if ((num % 2) > 0)
        {
            str = str + str3.Substring((int) Math.Round(Conversion.Val("&h" + str2.Substring(num, 1))), 1);
        }
        else
        {
            str = str + str4.Substring((int) Math.Round(Conversion.Val("&h" + str2.Substring(num, 1))), 1);
        }
        num++;
    }
    while (num <= 15);
    return (Regex.Replace(ConfigOpt.GetOption("Key"), @"\s*", "") == str);
}

Why MSTS/ORTS Need a Packaging Layer (and How Bethesda Solved the Same Problem)

For years, MSTS and ORTS users have struggled with filename collisions, missing assets, conflicting add‑ons, and content overwriting other content. These problems aren’t unique to train simulators — they appear in every long‑lived game ecosystem that relies on loose files and community add‑ons. The good news is that other communities have already solved this, and the solution is well understood.

To explain how MSTS/ORTS can move forward, it helps to look at a system that faced the same issues and built a robust solution: Bethesda’s modding ecosystem.

1. The Core Problem: Filenames as Identity

MSTS (and therefore ORTS) treat filenames as global, unique identifiers:

  • tree01.s means “the tree01 shape”
  • texture.ace means “the texture named texture.ace”
  • the simulator assumes these names are unique and correct

But in real life:

  • different creators reuse the same filenames
  • different routes ship different assets with identical names
  • repaints rely on payware shapes they cannot include
  • users install content in unpredictable orders
  • one add‑on overwrites another without warning

The simulator has no way to know:

  • which file is correct
  • whether two files with the same name are identical
  • whether a mismatch is a mistake or intentional
  • where an asset originally came from

This is not a runtime problem.
This is a content‑management problem.

And MSTS/ORTS have no content‑management layer.

2. Why ORTS Cannot Solve This at Runtime

By the time ORTS loads a route:

  • filenames are already fixed
  • world files already reference those filenames
  • shapes already reference their texture filenames
  • the simulator has no access to the original sources
  • the simulator cannot rename files or rewrite content safely

Trying to fix collisions inside the simulator would:

  • break MSTS compatibility
  • break saved games
  • break multiplayer determinism
  • produce different results for different users
  • make debugging impossible

A simulator cannot rewrite its own data files while loading them.
This is the wrong layer for the problem.

3. How Bethesda Faced the Same Problem

Bethesda’s games (Oblivion, Skyrim, Fallout, Starfield) had the exact same issues:

  • mods overwrote each other
  • assets with identical filenames conflicted
  • load order determined which files “won”
  • users had broken installs and missing assets
  • the game engine could not fix any of it

The engine was old, compatibility‑locked, and could not be changed.

So the community built a packaging and identity layer above the runtime:

Mod Organizer / Vortex / LOOT

These tools:

  • compute hashes for every asset
  • detect conflicts
  • track provenance
  • reorder assets
  • create virtual filesystems
  • isolate mods from each other
  • ensure deterministic installs
  • rewrite references when needed

The game engine itself does nothing.
It simply loads the final resolved files.

This is the exact pattern MSTS/ORTS need.

4. How the Same Solution Applies to MSTS/ORTS

The correct place to solve asset identity is:

A. During packaging

A packaging tool can:

  • compute hashes for shapes and textures
  • detect filename collisions
  • rename aliases safely
  • rewrite shapes to reference renamed textures
  • generate a metadata file (like a .ref) that records identity

This produces a clean, collision‑free, MSTS‑compatible route.

B. During installation

An installer can:

  • read the hash identities
  • fetch the correct assets from the user’s system
  • detect mismatches
  • rename assets deterministically
  • rewrite shapes and config files
  • ensure the installed route is self‑contained

This is exactly what Bethesda mod managers do.

C. During runtime

MSTS/ORTS simply:

  • load the files that exist
  • trust the filenames
  • behave deterministically

No hashing.
No guessing.
No rewriting.
No conflict resolution.

The simulator stays simple and compatible.

5. Why This Works for Repaints Too

Repaints often depend on payware shapes that cannot be redistributed.
The repaint can include:

  • textures
  • a modified .eng/.wag
  • a hash of the required shape

The installer then:

  • finds the correct shape
  • renames it if needed
  • rewrites the .eng/.wag
  • rewrites the .S file to use the repaint textures

This makes repaints legal, deterministic, and collision‑free.

6. The Pattern Is Universal

Whenever a runtime:

  • is old
  • is compatibility‑locked
  • cannot resolve identity
  • cannot rewrite content safely

…the ecosystem eventually builds a packaging + installer identity layer above it.

Linux did it.
Python did it.
Node.js did it.
Bethesda games did it.
Android did it.
Docker did it.

MSTS/ORTS are simply next in line.

7. The Bottom Line

MSTS/ORTS do not need to change.
The simulator should remain a deterministic runtime.

What the ecosystem needs is:

  • a packaging tool that computes hashes and resolves collisions
  • an installer that uses those hashes to fetch and prepare assets
  • stable filenames at runtime

This is the same solution that saved Bethesda’s modding ecosystem — and it will work just as well for MSTS/ORTS.

TD files

td files
each represents an area of 512 tiles by 512 tiles

-00026+00027.td
+00002+00021.td

67108864 = 8192 * 8192

512 tiles * 16 patches/tile = 8192 patches
each patch is 128m by 128m

depth 0 entire globe
divide in half by 6 times to get to depth 6
which is a 512 tiles square

-00022+00026.td
tdx tdz “.td”
x 11264 to 11775 from 512tdx to 512(tdx+1)-1
y 13312 to 13823 from 512 tdz to 512*(tdz+1)-1

world composed of 64 by 64 large TD tiles,
-32 to -1 and 0 to 31

depth tiles
0 32768
1 16384
2 8192
3 4096
4 2048
5 1024
6 512
7 256
8 128
9 64
10 32
11 16
12 8
13 4
14 2
15 1

td files start path in quad tree at depth 6 based on td_idx.dat

SIMISA@@@@@@@@@@JINX0D0t______

terrain_desc (
terrain_desc_size ( 67108864 )
Depth ( 6 )
terrain_desc_tiles (
TdFile ( -19 22 )
TdFile ( -1 21 )
TdFile ( 2 21 )
)
)

n number of decimal characters in tile name
if tile name starts with “-” then
depth = 2n-1 if tile name starts with “_” then depth = 2n

when converting tile name to tile numbers
if depth is odd then right shift two bits and discard

tile name to tile numbers
input: tile name
output: tile x, tile z, depth

tile numbers to tile name
input: tile x, tile z, depth
output: tile name

limit of tile numbers
minimum: -2^(depth-1)
maximum: 2^(depth-1)-1
example:
depth 5
minimum: -32
maximum: 31

TD file format

Hello,

here is the structure of the TD-file as far as it is known to me:

A TD-file (*.td,*.tdl) is connected with a tile-definition of 512×512 length. The position of this tile is shown within the filename.

The file starts with a header of 54 bytes, which contain:
– the file-id (+cr/lf)
– 3 length-values (datalength at pos 50, datalength+5 at pos. 45, datalength+14 at pos. 36)
– some (for me) unknown values

The next byte represents the 4 sub-tiles of this tile (length: 256×256). It shows, if the tile is ‘divided’ (first 4 bits) or populated (second 4 bits).
If none of the first 4 bits is set, the file ends here. Then it describes 4 tiles of length 256×256, which may be populated.

If one (or more) of the first 4 bits is set, this tile is divided and the next byte represents, the FIRST of these sub-tiles. It shows, if this tile is divided into subtiles of length 128×128 or if it is populated. The above scheme is used to signal this.

This procedure continues until sub-tiles of length 1×1 are reached, which cannot be further divided, or until a controlbyte is reached, where none of the first 4 bits is set.

Then, the second of the sub-tiles is processed in the same way. (one level higher)

This continues until all defined sub-tiles of various length are processed. Then, all bytes of the file should be processed, too.

I hope this will help you.

regards
CarlosHR

PS.
The 4 subtiles are processed (from-> to): upper/left, lower/left, lower/right, upper/right

Using the Simis Editor at http://jgrmsts.codeplex.com/ I was able to complete the format of TD files.

/* File format information */
FILE = :terrain_desc .
FILE_NAME = “Tile Definition” .
FILE_EXT = “td” .
FILE_TYPE = “d” .
FILE_TYPE_VER = “1” .

/* Base types */
terrain_desc_tiles ==> :uint {:byte} .

/* Format types */
terrain_desc ==> :terrain_desc_tiles .

EOF /* End of file */

The following are defined in coreids.tok:
SIDDEF(SID_TERRAIN_DESC, “terrain_desc”)
SIDDEF(SID_TERRAIN_DESC_FLAGS, “terrain_desc_flags”)
SIDDEF(SID_TERRAIN_DESC_SIZE, “terrain_desc_size”)
SIDDEF(SID_TERRAIN_DESC_TILES, “terrain_desc_tiles”)

SID_TERRAIN_DESC = 0x84
SID_TERRAIN_DESC_TILES = 0x87

Using the documentation for the binary format at http://twpol.dyndns.org/weblog/2010/05/10/01

32 byte header
4 byte identifier for SID_TERRAIN_DESC
4 byte length
1 byte length for null string
4 byte identifier for SID_TERRAIN_DESC_TILES
4 byte length
1 byte length for null string
4 byte :uint count of bytes
variable number of bytes describing division and population of tiles

Source: TD file – TrainSim.Com

Validate global tsection.dat

Errors

Track shapes that use an undefined track section

Track shape names must be unique.

Warnings

Track shapes that have two track sections start at the same location but missing MainRoute

Curved track sections are defined as a pair with negative angle followed by positive angle.

Junction track shapes were the two paths use the same track sections. One example is A1tYPnt10dMnl.s

TrackShape ( 217
FileName ( A1tYPnt10dMnl.s )
NumPaths ( 2 )
SectionIdx ( 2 0 0 0 0 183 175 )
SectionIdx ( 2 0 0 0 0 183 176 )
ManualJunctionShape ( )
)

Information

Track shapes that share the same definition of track sections.

Track shapes that do not have a shape file.

Functions

Validate a global tsection.dat file

Create a new trimmed file that only includes tracks that have a shape file.

Terrain files and elevations

Terrain files of type .t contain elevation and texture information. The structure and elevation data for normal size tiles:

terrain (
terrain_samples (
terrain_nsamples ( 256 )
terrain_sample_floor ( 934.403 )
terrain_sample_scale ( 0.00239484 )
terrain_sample_size ( 8 )
terrain_sample_ybuffer ( -01a186e8_y.raw )
terrain_sample_ebuffer ( -01a186e8_e.raw )
terrain_sample_nbuffer ( -01a186e8_n.raw )
)

And for lo tiles:

terrain (
terrain_samples (
terrain_nsamples ( 64 )
terrain_sample_floor ( 742.356 )
terrain_sample_scale ( 0.0241534 )
terrain_sample_size ( 256 )
terrain_sample_ybuffer ( _01a184_y.raw )
terrain_sample_ebuffer ( _01a184_e.raw )
terrain_sample_nbuffer ( _01a184_n.raw )
)

The tiles are a 2-dimensional grid of terrain_nsamples. The size of sample is terrain_sample_size in meters. The terrain_sample_ybuffer is an array of 2-byte unsigned integer in meters.

For normal tiles there are 256 by 256 samples each 8 meters, so the tile size is 2048 meters square. With each sample at 2 byte the file size is 131,072.

For lo tiles there are 64 by 64 samples each 256 meters, so the tile size is 16,384 meters square. With each sample at 2 bytes the file size is 8,192 bytes

When _y.raw has a value of 0 the elevation is terrain_sample_floor. To calculate the elevation at a sample multiply by terrain_sample_scale adding terrain_sample_floor. The maximum elevation is 65,536 times terrain_sample_scale plus terrain_sample_floor. The two bytes are in little endian order.

The name of the _y.raw file is in terrain_sample_ybuffer. Lo tiles have shorter names because they are earlier in the quad tree. Each quad tree division adds two bits to the file name of the tile as each division splits a tile into four parts.

Which tiles are split and selected is encoded in TD and TDL files in the TD directory.

The lo tiles will only be displayed in MSTS when mountains are active in the route definition file (.trk) with Mountains ( 00000001 ),

Converting between world and tile file names

World files have names with two numbers such as w-012489+014772.w and tile files have encoded names such as -01a4d768.t.

Code to convert world to tile file names

    function WorldNameToTileName(world)
      Dim x,z
      Dim tile
      if len(world) = 15 then
        x = mid(world,2,7)
        z = mid(world,9,7)
        If IsNumeric(x) And IsNumeric(z) then
          tile = WorldNamesToTileName(x,z)
        end if
      end if
      WorldNameToTileName = tile
    end function
    function WorldNamesToTileName (x,z)
      dim tile
      dim m
      dim wx,wz
      dim qx,qz,q
      dim i

      tile = 0
      m = 1
      wx = CInt(x) + 16384
      wz = CInt(z) + 16384
      for i = 0 to 14
        m = m * 4
        qx = wx and 1
        wx = CInt(wx \ 2)
        qz = wz and 1
        wz = CInt(wz \ 2)
        if qx = 1 then
          if qz = 1 then
            q = 1
          else
            q = 2
          end if
        else
          if qz = 1 then
            q = 0
          else
            q = 3
          end if
        end if
        tile = tile + (m * q)
      next
      WorldNamesToTileName = "-" & Right("0000000" & LCase(Hex(tile)),8)
    end Function

Code to convert tile to world file name

function TileNameToWorldName(sTileName)
    on error resume next
    dim x
    dim z
    dim m
    dim sTile
    dim tile
    dim q ' quandrant encoded in two bits NW=0, NE=1, SE=2, SW=3
    dim i
    dim sWorldName

    if left(sTileName,1) = "-" and len(sTileName) = 11 then
        sTile = mid(sTileName,2,8)
        tile = CLng("&h" & sTile) ' error occurs if file name includes non-hex characters
        if Err.Number = 0 then
            x = -16384
            z = -16384
            m = 1
            for i = 0 to 14
                tile = CLng(tile \ 2) ' shift 2 bits right
                tile = CLng(tile \ 2)
                q = tile and 3 ' extract right 2 bits
                if q = 1 or q = 2 then ' east quads NE and SE
                    x = x + m
                end if
                if q = 0 or q = 1 then ' north quads NW and NE
                    z = z + m
                end if
                m = m * 2
            next
            sWorldName = "w"
            if x < 0 then
                sWorldName = sWorldName & "-"
            else
                sWorldName = sWorldName & "+"
            end if
            sWorldName = sWorldName & right("00000" & Abs(x), 6)
            if z < 0 then
                sWorldName = sWorldName & "-"
            else
                sWorldName = sWorldName & "+"
            end if
            sWorldName = sWorldName & right("00000" & Abs(z), 6)
        else
            sWorldName = "ERROR: Invalid character in tile name."
            Err.Clear
        end if
    else
        sWorldName = "ERROR: Not a fully divided tile."
    end if
    TileNameToWorldName = sWorldName
end function

Shape files and Polymaster

Polymaster was created to fix certain problems with shape files created by Abacus Train Sim Modeler. Train Sim Modeler uses a reduced set of shape file (.s) syntax and Polymaster cannot read some valid shape files.

Archibald

vertex ( 00000000 0 8672 FFFFFFFF FF808080
vertex_uvs ( 1 0 ) )

Shape File Manager

vertex ( 00000000 0 8672 ffffffff ff808080
vertex_uvs ( 1 0 )
)

Polymaster can only read shape files where the vertex element uses three lines. If the closing parenthesis is on the second line, the shape file cannot be read.

Polymaster download https://www.thopil.de/werkstatt_pm.html