Ultima II Talk Format

From ModdingWiki
Jump to navigation Jump to search
Ultima II Talk Format
Format typeText
Text purposeStory
Line endingsCR
Character setASCII

The Ultima II Talk Format is an encrypted file which houses the dialogue from the game's NPCs. The files are named TLKX?? and have no file extension.

File format

Each file begins with a null, then contains four dialogue strings, each terminating with a null. In order to be displayed properly, each dialogue can be a maximum of three rows of text. Each row can be a maximum of 30 columns. Rows end with a carriage return unless they're 30 columns (a carriage return is implied), or they're the last row in the dialogue.

Every file is exactly 384 bytes in length. If the text in the file doesn't utilize all 384 bytes, the remainder of the file will contain garbage data from the memory buffer. There is even some source code to be found in some of the files!

The file uses an encryption similar to ROT13 (AKA the Caesar Cypher). However, instead of being rotated using a modulo 13, it is rotated by 128 (the length of the ASCII set).

Data type Name Description
BYTE Null Start of file null marker. Always 0x00.
ASCIIZ Dialogue Multi-lined dialogue text.

Source Code


The following FreeBASIC code will decrypt and print out the text in a specified Talk file.

' Decrypts and displays the text in Ultima 2 Talk files.

' Change this to the path and file name you want to view.
Open "H:\DOS\Ultima2\tlkx23" For Binary As #1

Screen 1

Dim As UShort Char
Dim As UByte ReadByte
Dim As UByte Column
Dim As UByte Row
Dim As UByte Dialogue

For Char = 1 To 384
    Get #1, , ReadByte

    If ReadByte = 0 Then
        ' The game engine uses a null character as a dialogue separator. 
        Dialogue = Dialogue + 1
        Column = 0
        Row = Row + 2
        ' Each Talk file has 4 dialogues. Any data after the fourth 
        ' dialogue contains garbage memory and shouldn't be read.
        If Dialogue = 5 Then
            Exit For
        End If
        ' Talk file uses encryption to obscure the game's dialogue. 
        ' It is similar to ROT13, but instead, you must subtract 128 
        ' from each character to get the ASCII value.
        ReadByte = ReadByte - 128

        ' The game display engine automatically wraps text at 30 
        ' characters, so, if we hit 31, we wrap the line as though we 
        ' hit a carriage return.
        Column = Column + 1
        If Column = 31 Then
            Column = 1
            Row = Row + 1
        End If

        Locate Row, Column
        Print Chr(ReadByte)

        If ReadByte = 13 Then
            ' Special trap for carriage return.
            Column = 0
            Row = Row + 1
        End If
    End If
Next Char

Close #1


This file format was reverse engineered by TheAlmightyGuru. If you find this information helpful in a project you're working on, please give credit where credit is due. (A link back to this wiki would be nice too!)