-- File: FixBootFileDates.mesa,  Last Edit: HGM,  December 10, 1980  1:21 AM

DIRECTORY
  Inline USING [LongDivMod],
  InlineDefs USING [BcplLongNumber, MesaToBcplLongNumber],
  Storage USING [String, FreeString],
  Time USING [Current],
  FormSW USING [
    AllocateItemDescriptor, ClientItemsProcType, CommandItem, newLine, ProcType,
    StringItem],
  Tool USING [Create, MakeSWsProc, MakeFormSW],
  ToolWindow USING [TransitionProcType],
  UserTerminal USING [BlinkDisplay],
  Window USING [Handle],
  File USING [Capability, nullCapability],
  George,
  BootServerDefs USING [BootFileHeader];

FixBootFileDates: PROGRAM
  IMPORTS Inline, InlineDefs, Storage, Time, FormSW, Tool, UserTerminal, George =
  BEGIN

  filename: STRING ← NIL;
  form: Window.Handle;

  Init: PROCEDURE =
    BEGIN
    [] ← Tool.Create[
      name: "BootFile Date Fixing Tool of December 9, 1980"L,
      makeSWsProc: MakeSWs, clientTransition: ClientTransition];
    END;

  Smash: FormSW.ProcType =
    BEGIN
    old, temp, new: George.Handle ← NIL;
    buffer: PACKED ARRAY [0..512) OF [0..377B];
    bfh: POINTER TO BootServerDefs.BootFileHeader = LOOPHOLE[@buffer];
    creation: POINTER TO InlineDefs.BcplLongNumber = LOOPHOLE[@buffer + 10];
    pages, bytes: CARDINAL;
    file, scratch: File.Capability ← George.NameToCapability[filename, 256];
    IF file = File.nullCapability THEN
      BEGIN UserTerminal.BlinkDisplay[]; RETURN; END;
    old ← George.CreateInputStream[file];
    scratch ← George.NameToCapability["FixBootFileDates$"L, 256];
    [pages, bytes] ← Inline.LongDivMod[George.GetLength[old], 512];
    George.SetIndex[old, 0];
    -- Copy the whole file into a scratch file
    temp ← George.CreateOutputStream[scratch];
    THROUGH [0..pages) DO
      IF George.GetWords[old, @buffer, 256] # 256 THEN ERROR;
      George.PutWords[temp, @buffer, 256];
      ENDLOOP;
    IF George.GetWords[old, @buffer, bytes/2] # bytes/2 THEN ERROR;
    George.PutWords[temp, @buffer, bytes/2];
    IF (bytes MOD 2) # 0 THEN George.PutByte[temp, George.GetByte[old]];
    George.Destroy[old];
    George.Destroy[temp];
    -- Copy it back, and fixup the header
    temp ← George.CreateInputStream[scratch];
    new ← George.CreateOutputStream[file];
    IF George.GetWords[temp, @buffer, 256] # 256 THEN ERROR;
    IF creation↑ = [0, 0] THEN creation↑ ← bfh.timeStamp;
    bfh.timeStamp ← InlineDefs.MesaToBcplLongNumber[Time.Current[]];
    George.PutWords[new, @buffer, 256]; -- Header or DiskBoot loader
    THROUGH [1..pages) DO
      IF George.GetWords[temp, @buffer, 256] # 256 THEN ERROR;
      George.PutWords[new, @buffer, 256];
      ENDLOOP;
    IF George.GetWords[temp, @buffer, bytes/2] # bytes/2 THEN ERROR;
    George.PutWords[new, @buffer, bytes/2];
    IF (bytes MOD 2) # 0 THEN George.PutByte[new, George.GetByte[temp]];
    George.Destroy[temp];
    George.Destroy[new];
    END;

  MakeSWs: Tool.MakeSWsProc =
    BEGIN form ← Tool.MakeFormSW[window: window, formProc: MakeForm]; END;

  MakeForm: FormSW.ClientItemsProcType =
    BEGIN
    nParams: CARDINAL = 2;
    items ← FormSW.AllocateItemDescriptor[nParams];
    items[0] ← FormSW.CommandItem[
      tag: "Smash"L, proc: Smash, place: FormSW.newLine];
    items[1] ← FormSW.StringItem[tag: "Filename"L, string: @filename];
    RETURN[items, TRUE];
    END;

  ClientTransition: ToolWindow.TransitionProcType =
    BEGIN
    SELECT TRUE FROM
      old = inactive => filename ← Storage.String[10*8];
      new = inactive => Storage.FreeString[filename];
      ENDCASE;
    END;

  Init[];
  END.