Ultima II Talk Format
Format type | Text |
---|---|
Text purpose | Story |
Line endings | CR |
Character set | ASCII |
Games |
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
Reader
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
Else
' 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
Sleep
Credits
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!)