Turbo Pascal Text Adventure Primer cover: BEACON, the adventure the primer builds in Turbo Pascal 2.0, mid-game on the Tatung Einstein
← Back to Modules
module
13

Straight To The Message

Introduction

S12's file holds everything, and makes the player wait up to two seconds for each message. The wait comes from reading the file from the start. A typed file, the kind the Turbo course's S21 kept records in, has no such problem: Seek goes straight to any record. So the messages move into one.

One Message, One Record

F: file of Str255;

Each record is one string[255], and message N is record N - 1, since records count from 0. To print message N:

Seek(F, N - 1);
Read(F, Msg);
Wrap(Msg);

Five reads of the first message take about a tenth of a second; five of the 27th, about a quarter of one. From the text file they took four seconds and ten.

Making The File

A program has to turn BEACON.TXT into the typed file once. MAKEMSG reads the text file line by line. Each * line starts a new message, so it writes the message it has been building, if any, and starts again. Each text line is added to the message, with a | between paragraphs so that Wrap can put the break back. The exits lines are left out.

program MakeMsg;
type
  Str255 = string[255];
var
  T: Text;
  F: file of Str255;
  Line, Msg: Str255;
  N: Integer;
begin
  Assign(T, 'BEACON.TXT');
  Reset(T);
  Assign(F, 'BEACON.MSG');
  Rewrite(F);
  N := 0;
  while not Eof(T) do
  begin
    Readln(T, Line);
    if Copy(Line, 1, 1) = '*' then
    begin
      if N > 0 then
        Write(F, Msg);
      N := N + 1;
      FillChar(Msg, SizeOf(Msg), 0);
    end
    else if (N > 0) and (Line <> '')
      and not (Line[1] in ['0'..'9'])
    then
    begin
      if Msg <> '' then
        Msg := Msg + '|';
      Msg := Msg + Line;
    end;
  end;
  Close(T);
  Writeln(FileSize(F), ' messages');
  Close(F);
end.

It prints 27 messages. BEACON.MSG is already on the primer's disc, made by exactly this program, so you need not run it; if you do, it makes the same file again.

Clearing The Record

FillChar(Msg, SizeOf(Msg), 0) fills all 256 bytes of Msg with zero, length byte and all, so Msg is the empty string. Why not simply Msg := ''?

Because Write(F, Msg) writes the whole variable: all 256 bytes, not just the characters in use. After Msg := '' the bytes past the length still hold the last message, and they go to the disc with the next one. A file of LAMP, SWORD and KEY written that way holds KEY followed by RD, left over from SWORD. The program never sees them, but they are on the disc. FillChar makes every unused byte zero.

The Cost On The Disc

A typed file of strings uses the declared length for every record. The 27 messages hold 2,652 characters between them, but the records take 6,912 bytes, and the file 7,040 on the disc. Over half of it is empty space.

On a disc with 110k free that does not matter, and a program that waits two seconds for every message matters a great deal. Part two's last two sections try to win the space back.

The Code

program SeekMsg;
type
  Str255 = string[255];
var
  F: file of Str255;
  Msg: Str255;

procedure Wrap(S: Str255);
var
  P: Integer;
begin
  S := S + '|';
  while S <> '' do
  begin
    P := Pos('|', S);
    if P > 40 then
    begin
      P := 40;
      while S[P] <> ' ' do
        P := P - 1;
    end;
    Writeln(Copy(S, 1, P - 1));
    Delete(S, 1, P);
  end;
end;

procedure Say(N: Integer);
begin
  Seek(F, N - 1);
  Read(F, Msg);
  Wrap(Msg);
end;

begin
  Assign(F, 'BEACON.MSG');
  Reset(F);
  Writeln('Go');
  Say(1);
  Say(27);
  Say(14);
  Close(F);
end.

Starting from

A new work file SEEKMSG, on the primer's disc.

What you should see

The same three messages as S12, with no pause before any of them. The logbook comes out in two paragraphs, broken where the | is:

...store, but the door is stuck fast
again."
"Oct 13. Gale. Going over to the
mainland for oil. ...

Change One Thing

  • Change Seek(F, N - 1) to Seek(F, N). What is printed first, and what happens when the program asks for message 27?
  • Change Say(14) to Say(28). There are 27 messages. What happens?
  • Change Writeln('Go') to Writeln(FileSize(F), ' messages').

Exercises

13.1 Print the length of every message, then the total, and how many bytes the records take altogether (FileSize(F) * SizeOf(Msg)).

13.2 Read message numbers typed at the keyboard and print each one, until 0. Ignore numbers that are not messages.

13.3 Copy every message out of BEACON.MSG into a new text file, MSG.TXT, one message to a line. Use D to see what it costs on the disc.

Worked solutions are in Appendix II.

When It Goes Wrong

SymptomCause
I/O error 99Reading a record past the end of the file. The messages are numbered 1 to 27, and the records 0 to 26.
Every message is the next one alongSeek(F, N) for Seek(F, N - 1).
I/O error 01BEACON.MSG is not on the disc. Run MAKEMSG (or S17's MAKEDAT) to make it.

Summary

A file of records goes straight to any message with Seek: a few hundredths of a second, against up to two seconds reading a text file. The price is space: every record is the full declared length, over half of it empty here. FillChar clears a record before it is filled, so nothing left over goes to the disc.

Next

S14, Three Letters In Two Bytes: winning the space back, Zork's way.

Get the Newsletter

New guides, disk images and community finds, roughly once a quarter. No spam, we promise, this isn't Tatung's marketing department.
Your subscription could not be saved. Please try again.
Your subscription has been successful.

Newsletter

Subscribe to our newsletter and stay updated.

This form is protected by reCAPTCHA, and the Google Privacy Policy and Terms of Service apply.