unit uScenViewer;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, uDefines, uTypeDefs, uHCDefines;

type
  TForm1 = class(TForm)
    OD1: TOpenDialog;
    Button1: TButton;
    Memo1: TMemo;
    edtFindAnnexID: TEdit;
    btnFindID: TButton;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure btnFindIDClick(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
    scnFile: TFileStream;
    AnnexCount: Cardinal; // The AnnexCounts added together
    AnnexCounts: packed array [0 .. NUM_ANNEXES - 1] of Word;
    AnnexIDs: packed array [0 .. NUM_ANNEXES - 1] of array of Word;
    // all of them, not by Annex
    AnnexMemRecCount: packed array [0 .. NUM_ANNEXES - 1] of Word;
    AnnexDiskRecCount: packed array [0 .. NUM_ANNEXES - 1] of Word;
    ScenarioGroupList: packed array of ScenarioGroup;
    GroupList1: GroupList;
    ScenarioUnitList: packed array of ScenarioUnit_v01;
    GameTimeLeft: Longint;
    VictoryConditions: packed array of VictoryCondition;
    function GetVersion: string;
    procedure CreateHarpoonEvent(path_leg: PScenarioPath;
      gidGroup_ID: Pgroup_id);
  end;

var
  Form1: TForm1;

implementation

uses uUtil, uGlobals, uHCEventHelpers, uHCTypeDefs;

{$R *.dfm}

{ *** Extracts the Delphi project version information *** }
function TForm1.GetVersion: string;
{ ---------------------------------------------------------
  Extracts the FileVersion element of the VERSIONINFO
  structure that Delphi maintains as part of a project's
  options.

  Results are returned as a standard string.  Failure
  is reported as "".

  Note that this implementation was derived from similar
  code used by Delphi to validate ComCtl32.dll.  For
  details, see COMCTRLS.PAS, line 3541.
  -------------------------------------------------------- }
const
  NOVIDATA = '';

var
  dwInfoSize, // Size of VERSIONINFO structure
  dwVerSize, // Size of Version Info Data
  dwWnd: DWORD; // Handle for the size call.
  FI: PVSFixedFileInfo; // Delphi structure; see WINDOWS.PAS
  ptrVerBuf: Pointer; // pointer to a version buffer
  strFileName, // Name of the file to check
  strVersion: string; // Holds parsed version number
begin

  strFileName := paramStr(0);
  dwInfoSize := getFileVersionInfoSize(pChar(strFileName), dwWnd);

  if (dwInfoSize = 0) then
    result := NOVIDATA
  else
  begin

    getMem(ptrVerBuf, dwInfoSize);
    try

      if getFileVersionInfo(pChar(strFileName), dwWnd, dwInfoSize, ptrVerBuf)
      then

        if verQueryValue(ptrVerBuf, '\', Pointer(FI), dwVerSize) then

          strVersion := format('%d.%d.%d.%d', [hiWord(FI.dwFileVersionMS),
            loWord(FI.dwFileVersionMS), hiWord(FI.dwFileVersionLS),
            loWord(FI.dwFileVersionLS)]);

    finally
      freeMem(ptrVerBuf);
    end;
  end;
  result := strVersion;
end;
{ *** end GetVersion *** }

procedure TForm1.Button1Click(Sender: TObject);
var
  FileName: string;
  intNumShips: integer;
  intNumLegs: Smallint;
  intLeg: NativeUInt;
  intNumEvents: Smallint;
  intEvent: NativeUInt;
  intVerScen: Smallint;
  HCDBVer: DATABASE_VERSION;
  AIStayOnPath: THCAI_STAYONPATH;
  dt_scen_start: THCDateTimeRec;
  NUM_GROUPS, NUM_UNITS, NUM_PLANES, NUM_HAR_EVENTS, NUM_VCS, NUM_PATHS: Word;
  intC1, intC2: integer;
  strTemp: string;
  strAnnex: string;
  ScenUnit_v00: ScenarioUnit_v00;
  ScenUnit_v01: ScenarioUnit_v01;
  ptrParentPathOffset: Pointer;
  gidGroup_ID: group_id;
  unitPath: ScenarioPathList;
  stlPlaneCounts: TStringList;
  // ALE 20140329 Fun statistic, # of planes by type

begin
  if OD1.Execute then
  begin
    stlPlaneCounts := TStringList.Create;

    Memo1.Lines.Clear;

    FileName := OD1.FileName;

    scnFile := TFileStream.Create(FileName, fmOpenRead OR fmShareDenyNone);
    // Check first two bytes to see if we have a scen version
    scnFile.Seek(0, 0);
    scnFile.ReadBuffer(intVerScen, SizeOf(intVerScen));
    // if value is -1 then there is a scenario version, otherwise version 0
    // aka non EC2003+ scenario
    if (intVerScen = -1) then
    begin
      // Version is 1 or greater so get version
      scnFile.ReadBuffer(intVerScen, SizeOf(intVerScen));
    end
    else
    begin
      intVerScen := 0;
    end;
    Memo1.Lines.Add(FileName + ' Scenario Version: ' + IntToStr(intVerScen));

    // If version 2 scenario or greater, read the database version, otherwise
    // there is no DB version
    if (intVerScen >= 2) then
    begin
      scnFile.ReadBuffer(HCDBVer, SizeOf(HCDBVer));
      Memo1.Lines.Add('DB Version RES: ' + HCDBVer.RESFile.flavor);
      Memo1.Lines.Add('DB Version RSR: ' + HCDBVer.RSRFile.flavor);
      // ALE20081103 added
    end
    else if (intVerScen = 1) then
    begin
      // seek back to byte 5 though really it is already there
      scnFile.Seek(5, 0);
    end
    else
    begin
      // version 0, seek back to beginning of file
      scnFile.Seek(0, 0);
    end;

    // ALE 20151002 add in version 3 data
    if (intVerScen >= 3) then
		begin
      // ALE 20150610 Determine if AI should stay on Scenario Designer created path or not
      scnFile.ReadBuffer(AIStayOnPath, SizeOf(AIStayOnPath));
			// ALE 20150616 Grab the scenario start date & time
      scnFile.ReadBuffer(dt_scen_start, SizeOf(dt_scen_start));
		end;

    // Read count of items from each annex
    scnFile.ReadBuffer(AnnexCounts, SizeOf(AnnexCounts));

    Memo1.Lines.Add('SHIP_FLAG Count: ' + IntToStr(AnnexCounts[SHIP_FLAG]));
    Memo1.Lines.Add('SUB_FLAG Count: ' + IntToStr(AnnexCounts[SUB_FLAG]));
    Memo1.Lines.Add('PLANE_FLAG Count: ' + IntToStr(AnnexCounts[PLANE_FLAG]));
    Memo1.Lines.Add('GUN_FLAG Count: ' + IntToStr(AnnexCounts[GUN_FLAG]));
    Memo1.Lines.Add('MISSILE_FLAG Count: ' +
      IntToStr(AnnexCounts[MISSILE_FLAG]));
    Memo1.Lines.Add('ASW_FLAG Count: ' + IntToStr(AnnexCounts[ASW_FLAG]));
    Memo1.Lines.Add('TORP_FLAG Count: ' + IntToStr(AnnexCounts[TORP_FLAG]));
    Memo1.Lines.Add('ATOG_FLAG Count: ' + IntToStr(AnnexCounts[ATOG_FLAG]));
    Memo1.Lines.Add('ECM_FLAG Count: ' + IntToStr(AnnexCounts[ECM_FLAG]));
    Memo1.Lines.Add('NUKE_FLAG Count: ' + IntToStr(AnnexCounts[NUKE_FLAG]));
    Memo1.Lines.Add('RADAR_FLAG Count: ' + IntToStr(AnnexCounts[RADAR_FLAG]));
    Memo1.Lines.Add('SONAR_FLAG Count: ' + IntToStr(AnnexCounts[SONAR_FLAG]));
    Memo1.Lines.Add('BASE_FLAG Count: ' + IntToStr(AnnexCounts[BASE_FLAG]));
    Memo1.Lines.Add('LOADOUT_FLAG Count: ' +
      IntToStr(AnnexCounts[LOADOUT_FLAG]));
    Memo1.Lines.Add('MOUNT_FLAG Count: ' + IntToStr(AnnexCounts[MOUNT_FLAG]));
    Memo1.Lines.Add('MULTI_FLAG Count: ' + IntToStr(AnnexCounts[MULTI_FLAG]));

    // Add up all of the annex counts so that we can read the DiskIDs
    AnnexCount := 0; // reset
    for intC1 := 0 to NUM_ANNEXES - 1 do
    begin
      AnnexCount := AnnexCount + AnnexCounts[intC1];
    end;
    Memo1.Lines.Add('AnnexCount: ' + IntToStr(AnnexCount));

    // Read AnnexIDs per annex
    for intC1 := 0 to NUM_ANNEXES - 1 do
    begin
      SetLength(AnnexIDs[intC1], AnnexCounts[intC1]);
      if (AnnexCounts[intC1] > 0) then
      begin
        for intC2 := 0 to AnnexCounts[intC1] - 1 do
        begin
          scnFile.ReadBuffer(AnnexIDs[intC1][intC2], SizeOf(Word));
        end;
      end;
    end;

    strTemp := 'Ship Annex IDs: ';
    for intC1 := System.Low(AnnexIDs[SHIP_FLAG]) to System.High(AnnexIDs[SHIP_FLAG]) do
    begin
      strTemp := strTemp + IntToStr(AnnexIDs[SHIP_FLAG][intC1]) + ',';
    end;
    Memo1.Lines.Add(Copy(strTemp, 1, Length(strTemp) - 1));
    strTemp := 'Sub Annex IDs: ';
    for intC1 := System.Low(AnnexIDs[SUB_FLAG]) to System.High(AnnexIDs[SUB_FLAG]) do
    begin
      strTemp := strTemp + IntToStr(AnnexIDs[SUB_FLAG][intC1]) + ',';
    end;
    Memo1.Lines.Add(Copy(strTemp, 1, Length(strTemp) - 1));
    strTemp := 'Plane Annex IDs: ';
    for intC1 := System.Low(AnnexIDs[PLANE_FLAG]) to System.High(AnnexIDs[PLANE_FLAG]) do
    begin
      strTemp := strTemp + IntToStr(AnnexIDs[PLANE_FLAG][intC1]) + ',';
    end;
    Memo1.Lines.Add(Copy(strTemp, 1, Length(strTemp) - 1));
    strTemp := 'Gun Annex IDs: ';
    for intC1 := System.Low(AnnexIDs[GUN_FLAG]) to System.High(AnnexIDs[GUN_FLAG]) do
    begin
      strTemp := strTemp + IntToStr(AnnexIDs[GUN_FLAG][intC1]) + ',';
    end;
    Memo1.Lines.Add(Copy(strTemp, 1, Length(strTemp) - 1));
    strTemp := 'Missile Annex IDs: ';
    for intC1 := System.Low(AnnexIDs[MISSILE_FLAG]) to System.High(AnnexIDs[MISSILE_FLAG]) do
    begin
      strTemp := strTemp + IntToStr(AnnexIDs[MISSILE_FLAG][intC1]) + ',';
    end;
    Memo1.Lines.Add(Copy(strTemp, 1, Length(strTemp) - 1));
    strTemp := 'ASW Annex IDs: ';
    for intC1 := System.Low(AnnexIDs[ASW_FLAG]) to System.High(AnnexIDs[ASW_FLAG]) do
    begin
      strTemp := strTemp + IntToStr(AnnexIDs[ASW_FLAG][intC1]) + ',';
    end;
    Memo1.Lines.Add(Copy(strTemp, 1, Length(strTemp) - 1));
    strTemp := 'Torp Annex IDs: ';
    for intC1 := System.Low(AnnexIDs[TORP_FLAG]) to System.High(AnnexIDs[TORP_FLAG]) do
    begin
      strTemp := strTemp + IntToStr(AnnexIDs[TORP_FLAG][intC1]) + ',';
    end;
    Memo1.Lines.Add(Copy(strTemp, 1, Length(strTemp) - 1));
    strTemp := 'Air to Ground Annex IDs: ';
    for intC1 := System.Low(AnnexIDs[ATOG_FLAG]) to System.High(AnnexIDs[ATOG_FLAG]) do
    begin
      strTemp := strTemp + IntToStr(AnnexIDs[ATOG_FLAG][intC1]) + ',';
    end;
    Memo1.Lines.Add(Copy(strTemp, 1, Length(strTemp) - 1));
    strTemp := 'ECM Annex IDs: ';
    for intC1 := System.Low(AnnexIDs[ECM_FLAG]) to System.High(AnnexIDs[ECM_FLAG]) do
    begin
      strTemp := strTemp + IntToStr(AnnexIDs[ECM_FLAG][intC1]) + ',';
    end;
    Memo1.Lines.Add(Copy(strTemp, 1, Length(strTemp) - 1));
    strTemp := 'Nuke Annex IDs: ';
    for intC1 := System.Low(AnnexIDs[NUKE_FLAG]) to System.High(AnnexIDs[NUKE_FLAG]) do
    begin
      strTemp := strTemp + IntToStr(AnnexIDs[NUKE_FLAG][intC1]) + ',';
    end;
    Memo1.Lines.Add(Copy(strTemp, 1, Length(strTemp) - 1));
    strTemp := 'Radar Annex IDs: ';
    for intC1 := System.Low(AnnexIDs[RADAR_FLAG]) to System.High(AnnexIDs[RADAR_FLAG]) do
    begin
      strTemp := strTemp + IntToStr(AnnexIDs[RADAR_FLAG][intC1]) + ',';
    end;
    Memo1.Lines.Add(Copy(strTemp, 1, Length(strTemp) - 1));
    strTemp := 'Sonar Annex IDs: ';
    for intC1 := System.Low(AnnexIDs[SONAR_FLAG]) to System.High(AnnexIDs[SONAR_FLAG]) do
    begin
      strTemp := strTemp + IntToStr(AnnexIDs[SONAR_FLAG][intC1]) + ',';
    end;
    Memo1.Lines.Add(Copy(strTemp, 1, Length(strTemp) - 1));
    strTemp := 'Installation Annex IDs: ';
    for intC1 := System.Low(AnnexIDs[BASE_FLAG]) to System.High(AnnexIDs[BASE_FLAG]) do
    begin
      strTemp := strTemp + IntToStr(AnnexIDs[BASE_FLAG][intC1]) + ',';
    end;
    Memo1.Lines.Add(Copy(strTemp, 1, Length(strTemp) - 1));
    strTemp := 'Loadout Annex IDs: ';
    for intC1 := System.Low(AnnexIDs[LOADOUT_FLAG]) to System.High(AnnexIDs[LOADOUT_FLAG]) do
    begin
      strTemp := strTemp + IntToStr(AnnexIDs[LOADOUT_FLAG][intC1]) + ',';
    end;
    Memo1.Lines.Add(Copy(strTemp, 1, Length(strTemp) - 1));
    strTemp := 'Mount Annex IDs: ';
    for intC1 := System.Low(AnnexIDs[MOUNT_FLAG]) to System.High(AnnexIDs[MOUNT_FLAG]) do
    begin
      strTemp := strTemp + IntToStr(AnnexIDs[MOUNT_FLAG][intC1]) + ',';
    end;
    Memo1.Lines.Add(Copy(strTemp, 1, Length(strTemp) - 1));
    strTemp := 'MultiMount Annex IDs: ';
    for intC1 := System.Low(AnnexIDs[MULTI_FLAG]) to System.High(AnnexIDs[MULTI_FLAG]) do
    begin
      strTemp := strTemp + IntToStr(AnnexIDs[MULTI_FLAG][intC1]) + ',';
    end;
    Memo1.Lines.Add(Copy(strTemp, 1, Length(strTemp) - 1));

    // Here we should recalculate AnnexIDs in case database is slightly different

    // Now grab number of group, units, plane chunks, harpoon event counts
    scnFile.ReadBuffer(NUM_GROUPS, SizeOf(NUM_GROUPS));
    scnFile.ReadBuffer(NUM_UNITS, SizeOf(NUM_UNITS));
    scnFile.ReadBuffer(NUM_PLANES, SizeOf(NUM_PLANES));
    scnFile.ReadBuffer(NUM_HAR_EVENTS, SizeOf(NUM_HAR_EVENTS));

    Memo1.Lines.Add('NUM_GROUPS: ' + IntToStr(NUM_GROUPS));
    Memo1.Lines.Add('NUM_UNITS: ' + IntToStr(NUM_UNITS));
    Memo1.Lines.Add('NUM_PLANES: ' + IntToStr(NUM_PLANES));
    Memo1.Lines.Add('NUM_HAR_EVENTS: ' + IntToStr(NUM_HAR_EVENTS));

    // Read in groups
    SetLength(ScenarioGroupList, NUM_GROUPS);
    for intC1 := 0 to NUM_GROUPS - 1 do
    begin
      scnFile.ReadBuffer(ScenarioGroupList[intC1], SizeOf(ScenarioGroup));
    end;

    // Convert ScenarioGroups to Groups
    SetLength(GroupList1, NUM_GROUPS);
    for intC1 := 0 to NUM_GROUPS - 1 do
    begin
      GroupList1[intC1].sLat := ScenarioGroupList[intC1].initLat;
      GroupList1[intC1].sLon := ScenarioGroupList[intC1].initLon;
      GroupList1[intC1].side := ScenarioGroupList[intC1].side;
      Move(ScenarioGroupList[intC1].zones, GroupList1[intC1].zones,
        SizeOf(Byte) * Length(GroupList1[intC1].zones));
      //GroupList1[intC1].percent_chance := ScenarioGroupList[intC1].percent_chance;
    end;

    // ALE 20081117 assign group ID
    for intC1 := 0 to NUM_GROUPS - 1 do
    begin
      assign_group_id(GroupList1[intC1], GroupList1);
    end;

    for intC1 := 0 to NUM_GROUPS - 1 do
    begin
      Memo1.Lines.Add('Group' + format('%0.4d', [intC1]) + '  ID: ' +
        GroupList1[intC1].id + '  Lat: ' + format('%0.10d',
        [GroupList1[intC1].sLat]) + '  Lon: ' + format('%0.10d',
        [GroupList1[intC1].sLon]) + '  InclusionChance: ' + format('%0.3d',
        [ScenarioGroupList[intC1].percent_chance]) + '  Formation Zones: ' +
        format('%0.3d', [GroupList1[intC1].zones[0]]) + ' ' + format('%0.3d',
        [GroupList1[intC1].zones[1]]) + ' ' + format('%0.3d',
        [GroupList1[intC1].zones[2]]) + ' ' + format('%0.3d',
        [GroupList1[intC1].zones[3]]) + '  Side: ' +
        SideToStr(GroupList1[intC1].side));
    end;

    // Read in units
    SetLength(ScenarioUnitList, (NUM_UNITS + NUM_PLANES));
    for intC1 := 0 to (NUM_UNITS + NUM_PLANES) - 1 do
    begin
      if intVerScen = 0 then
      begin
        scnFile.ReadBuffer(ScenUnit_v00, SizeOf(ScenarioUnit_v00));
        ScenarioUnitList[intC1] := ScenUnit_v00_2_v01(ScenUnit_v00);
      end
      else
      begin
        scnFile.ReadBuffer(ScenUnit_v01, SizeOf(ScenarioUnit_v01));
        ScenarioUnitList[intC1] := ScenUnit_v01;
      end;
    end;

    for intC1 := 0 to (NUM_UNITS + NUM_PLANES) - 1 do
    begin
      if (ANNEX(ScenarioUnitList[intC1].disk_id) = ANNEX_A1) then
        strAnnex := 'Ship'
      else if (ANNEX(ScenarioUnitList[intC1].disk_id) = ANNEX_A2) then
        strAnnex := 'Sub'
      else if (ANNEX(ScenarioUnitList[intC1].disk_id) = ANNEX_B) then
        strAnnex := 'Plane'
      else if (ANNEX(ScenarioUnitList[intC1].disk_id) = ANNEX_M1) then
        strAnnex := 'Installation'
      else
        strAnnex := '';

      // ALE 20140329 count up planes of each type
      if (ANNEX(ScenarioUnitList[intC1].disk_id) = ANNEX_B) then
      begin
        if (stlPlaneCounts.IndexOfName(ScenarioUnitList[intC1].UnitName) = -1)
        then
        begin
          stlPlaneCounts.Add(ScenarioUnitList[intC1].UnitName + '=' +
            IntToStr(ScenarioUnitList[intC1].PlatStart));
        end
        else
        begin
          stlPlaneCounts.Values[ScenarioUnitList[intC1].UnitName] :=
            IntToStr(StrToInt(stlPlaneCounts.Values[ScenarioUnitList[intC1]
            .UnitName]) + ScenarioUnitList[intC1].PlatStart);
        end;
      end;

      Memo1.Lines.Add('Unit' + format('%0.4d', [intC1]) + '  xOff: ' +
        format('%0.10d', [ScenarioUnitList[intC1].xOff]) + '  yOff: ' +
        format('%0.10d', [ScenarioUnitList[intC1].yOff]) + '  DiskID: ' +
        format('%0.5d', [ScenarioUnitList[intC1].disk_id]) + '  GroupID: ' +
        format('%s', [ScenarioUnitList[intC1].parent_id]) + '  ParentUnit: ' +
        format('%0.3d', [ScenarioUnitList[intC1].parent_base]) + '  PlatStart: '
        + format('%0.3d', [ScenarioUnitList[intC1].PlatStart]) + '  Annex: ' +
        format('%s', [strAnnex]) + '  PercentChance: ' + format('%0.3d',
        [ScenarioUnitList[intC1].percent_chance]) + '  UnitName: ' +
        format('%s', [ScenarioUnitList[intC1].UnitName]));
    end;

    // Scenario duration
    scnFile.ReadBuffer(GameTimeLeft, SizeOf(GameTimeLeft));
    Memo1.Lines.Add('GameTimeLeft: ' + IntToStr(GameTimeLeft));

    // Grab Victory Conditions
    scnFile.ReadBuffer(NUM_VCS, SizeOf(NUM_VCS));
    Memo1.Lines.Add('NUM_VCS: ' + IntToStr(NUM_VCS));
    // Read in Victory Conditions
    SetLength(VictoryConditions, NUM_VCS);
    for intC1 := 0 to NUM_VCS - 1 do
    begin
      scnFile.ReadBuffer(VictoryConditions[intC1], SizeOf(VictoryCondition));
    end;
    for intC1 := 0 to NUM_VCS - 1 do
    begin
      Memo1.Lines.Add('VC' + format('%0.4d', [intC1]) + '  TimeOnStation: ' +
        format('%0.10d', [VictoryConditions[intC1].TimeOnStation]) +
        '  DetailType: ' + format('%0.5d', [VictoryConditions[intC1].DetailType]
        ) + '  AreaOnStation Left: ' + format('%0.5d',
        [VictoryConditions[intC1].AreaOnStation.left]) +
        '  AreaOnStation Right: ' + format('%0.5d',
        [VictoryConditions[intC1].AreaOnStation.right]) +
        '  AreaOnStation Top: ' + format('%0.5d',
        [VictoryConditions[intC1].AreaOnStation.top]) +
        '  AreaOnStation Bottom: ' + format('%0.5d',
        [VictoryConditions[intC1].AreaOnStation.bottom]) + '  ID: ' +
        format('%0.3d', [VictoryConditions[intC1].id]) + '  VicType: ' +
        format('%0.3d', [VictoryConditions[intC1].VicType]) + '  AndOrFlag: ' +
        format('%0.3d', [VictoryConditions[intC1].AndOrFlag]) + '  WhichSide: '
        + format('%0.3d', [VictoryConditions[intC1].WhichSide]) + '  unitType: '
        + format('%0.3d', [VictoryConditions[intC1].unitType]) + '  itemType: '
        + format('%0.3d', [VictoryConditions[intC1].itemType]) +
        '  percentDamage: ' + format('%0.3d',
        [VictoryConditions[intC1].percentDamage]) + '  numKilled: ' +
        format('%0.3d', [VictoryConditions[intC1].numKilled]) + '  numDamaged: '
        + format('%0.3d', [VictoryConditions[intC1].numDamaged]) + '  Dummy: ' +
        format('%0.3d', [VictoryConditions[intC1].Dummy]));
    end;

    // Grab Course Paths
    scnFile.ReadBuffer(NUM_PATHS, SizeOf(NUM_PATHS));
    Memo1.Lines.Add('NUM_PATHS: ' + IntToStr(NUM_PATHS));
    for intC1 := 0 to NUM_PATHS - 1 do
    begin
      scnFile.ReadBuffer(ptrParentPathOffset, SizeOf(ptrParentPathOffset));
      scnFile.ReadBuffer(intNumLegs, SizeOf(intNumLegs));
      scnFile.ReadBuffer(gidGroup_ID, SizeOf(gidGroup_ID));
      // Read in the path legs
      SetLength(unitPath, intNumLegs);
      for intLeg := 0 to intNumLegs - 1 do
      begin
        scnFile.ReadBuffer(unitPath[intLeg], SizeOf(ScenarioPath));
        Memo1.Lines.Add('PathLeg ' + format('%0.4d', [intLeg]) + '  Group: ' +
          format('%s', [gidGroup_ID]) + '  DestLat: ' + format('%0.10d',
          [unitPath[intLeg].DestLat]) + '  DestLon: ' + format('%0.10d',
          [unitPath[intLeg].DestLon]) + '  Events@Dest: ' + format('%0.5d',
          [unitPath[intLeg].num_events]));
        if (unitPath[intLeg].num_events > 0) then
        begin
          for intEvent := 0 to unitPath[intLeg].num_events - 1 do
          begin
            CreateHarpoonEvent(nil, @gidGroup_ID);
          end;
        end;
      end;
    end;

    // ALE 20140406 Grab Harpoon Events not associated with a path leg
    for intC1 := 0 to NUM_HAR_EVENTS - 1 do
    begin
      CreateHarpoonEvent(nil, nil);
    end;

    // Do other fun stuff
    Memo1.Lines.Add('Plane Class Counts:');
    stlPlaneCounts.Sort;
    for intC1 := 0 to stlPlaneCounts.Count - 1 do
    begin
      Memo1.Lines.Add(stlPlaneCounts[intC1]);
    end;

    scnFile.Free;
    stlPlaneCounts.Free;
    // ShowMessage(IntToStr(intNumShips) + ' Ships');
  end;
  btnFindID.Enabled := True;
  ShowMessage('All Done');
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  Form1.Caption := 'HC Scen Viewer ' + GetVersion;
end;

procedure TForm1.btnFindIDClick(Sender: TObject);
var
  intC1: integer;
  blnFound: boolean;
begin
  blnFound := False;
  for intC1 := 0 to Memo1.Lines.Count - 1 do
  begin
    if Pos(edtFindAnnexID.Text, Memo1.Lines[intC1]) > 0 then
    begin
      ShowMessage('AnnexID ' + edtFindAnnexID.Text + ' is in scenario');
      blnFound := True;
      Memo1.SetFocus;
      Memo1.SelStart := Pos(edtFindAnnexID.Text, Memo1.Lines.Text) - 1;
      Memo1.SelLength := Length(edtFindAnnexID.Text);
      exit;
    end
  end;
  if (blnFound = False) then
  begin
    ShowMessage('AnnexID ' + edtFindAnnexID.Text + ' is not in scenario');
    Memo1.SetFocus;
    Memo1.ClearSelection;

  end;
end;

procedure TForm1.CreateHarpoonEvent(path_leg: PScenarioPath;
  gidGroup_ID: Pgroup_id);
var
  acculumated_percent: Shortint;
  die_roll: Byte;
  event_type: Byte;
  ignore_event: Byte;
  stay_on_parent_path: Byte;
  ignore_friendly_event: Byte;
  scen_har_event: ScenarioHarpoonEvent;
  theGroup: group_id;
  EventData: Pointer;
  PE_Alt_Speed: PHCSpeedAltEvent;
begin
  // from SE-only portions of
  // uchar CreateHarpoonEvent(PathRecPtr path_leg, GroupPtr my_group)
  ignore_event := bFALSE;
  stay_on_parent_path := bTRUE;
  ignore_friendly_event := bFALSE;

  // TODO	ScenarioHarpoonEventPtr       scen_har_event_ptr = &scen_har_event;

  // CLOG(LOAD_LOG, "\t\t\tScenarioResPtr=%p\n", ScenarioResPtr);
  // ALE 20130724 prevent Memory Allocation Error BlockMove(ScenarioResPtr, scen_har_event_ptr, sizeof(*scen_har_event_ptr));
  // scen_har_event_ptr = (ScenarioHarpoonEventPtr)ReadStruct(ScenarioResPtr, sizeof(ScenarioHarpoonEvent));
  scnFile.ReadBuffer(scen_har_event, SizeOf(ScenarioHarpoonEvent));

  event_type := scen_har_event.flag AND CommandMask;
  if (gidGroup_ID = nil) then // not in path rec
  begin
    // CLOG(LOAD_LOG, "in the my_group path==NULL path\n");
    if (scen_har_event.grp_id <> nil) then // group supposed to be non-null
    begin
      theGroup := scen_har_event.grp_id;
      if (theGroup = nil) then // error in scenario file
      begin
        // harpoon_event.DataPtr := nil;
        ignore_event := bTRUE;
        // CLOG(LOAD_LOG, "NOTE: ignoring event because IdtoGroupPtr(%s) == NULL\n", scen_har_event_ptr->group_id);
      end;
    end
    else
    begin
      // CLOG(LOAD_LOG, "ERROR: my_group==NULL, then scen_har_event_ptr->group_id[0] == EOS\n");
    end;
  end;

  if (ignore_event = bFALSE) then
  begin
    // harpoon_event.ourGroup = my_group;
    // harpoon_event.UnitID   = scen_har_event_ptr->unit_id;
    // harpoon_event.Time     = scen_har_event_ptr->Time;
    // harpoon_event.flag     = scen_har_event_ptr->flag;
    // event_type             = harpoon_event.flag & CommandMask;

    // CLOG(LOAD_LOG, "!ignore_event, ourgroup=%p, GroupID=%s UnitID=%u, time=%lu, flag=%u, type=%u\n", harpoon_event.ourGroup, scen_har_event_ptr->group_id, harpoon_event.UnitID, harpoon_event.Time, harpoon_event.flag, event_type);
    if (gidGroup_ID = nil) then // not in path rec
    begin
      Memo1.Lines.Add('Event' + '  Group: ' + format('%s',
        [scen_har_event.grp_id]) + '  Unit: ' + format('%0.4d',
        [scen_har_event.unit_id]) + '  Time: ' + format('%0.10d',
        [scen_har_event.Time]) + '  Type: ' + format('%s',
        [HCEventType2String(scen_har_event.flag AND CommandMask)]) + '  Flag: '
        + format('%0.3d', [scen_har_event.flag])

        + '  Dummy: ' + format('%0.3d', [scen_har_event.Dummy]) + '  Dummy2: ' +
        format('%0.3d', [scen_har_event.Dummy2]) + '  DataSize: ' +
        format('%0.5d', [scen_har_event.data_size]));
    end
    else
    begin
      Memo1.Lines.Add('PathLeg Event' + '  Group: ' + format('%s',
        [scen_har_event.grp_id]) + '  Unit: ' + format('%0.4d',
        [scen_har_event.unit_id]) + '  Time: ' + format('%0.10d',
        [scen_har_event.Time]) + '  Type: ' + format('%s',
        [HCEventType2String(scen_har_event.flag AND CommandMask)]) + '  Flag: '
        + format('%0.3d', [scen_har_event.flag])

        + '  Dummy: ' + format('%0.3d', [scen_har_event.Dummy]) + '  Dummy2: ' +
        format('%0.3d', [scen_har_event.Dummy2]) + '  DataSize: ' +
        format('%0.5d', [scen_har_event.data_size]));
    end;

    if (scen_har_event.data_size > 0) then
    begin
      // ALE 20130724 prevent Memory Allocation Error harpoon_event.DataPtr  = NewPtr((long)scen_har_event_ptr->data_size);
      // BlockMove((FarPtr)ScenarioResPtr,harpoon_event.DataPtr,
      // (long)scen_har_event_ptr->data_size);
      // harpoon_event.DataPtr = ReadStruct(ScenarioResPtr, (long)scen_har_event_ptr->data_size);
      if (event_type = ALT_SPEED_EVENT) then
      begin
        scnFile.Read(PE_Alt_Speed, scen_har_event.data_size);
      end
      else
      begin
        scnFile.Seek(scen_har_event.data_size, soFromCurrent);
      end;

    end;
  end;

  // fixme: if ignore_event is true, event_type may not have been set above
  // need to deal with it, but since it is only unset in an error case, it
  // probably doesn't matter (and besides, event_type probably != ALT_PATH_EVENT
  // when undefine
  if (event_type <> ALT_PATH_EVENT) then
  begin
    // Delphi automatically moves the block forwardScenarioResPtr += scen_har_event_ptr->data_size;
  end
  else
  begin
    // ALE 20140321 We have to roll-back the stream to pick up the alternate
    // path
    Memo1.Lines.Add('!!!AltPathEvent!!!');
    {
      AlternatePathEventPtr   scen_alt_path_ptr;
      PathRecPtr              save_group_path;

      CLOG(LOAD_LOG, "Scenario editor is handling an ALT_PATH_EVENT\n");

      ScenarioResPtr        += scen_har_event_ptr->data_size;
      scen_alt_path_ptr = (AlternatePathEventPtr)harpoon_event.DataPtr;

      /*------------------*/
      /* skip path offset */
      /*------------------*/

      ScenarioResPtr               += sizeof(long);

      save_group_path               = harpoon_event.ourGroup->track;
      harpoon_event.ourGroup->track = NULL;
      CreatePath();

      scen_alt_path_ptr->PathHead   = harpoon_event.ourGroup->track;
      harpoon_event.ourGroup->track = save_group_path;
    }
  end;

  if (ignore_event = bTRUE) then
  begin
    {
      CLOG(LOAD_LOG, "ignoring event\n");
      if (harpoon_event.DataPtr)
      DisposPtr(harpoon_event.DataPtr);
      harpoon_event.DataPtr = NULL; //gw 10/96
    }
  end
  else
  begin
    {
      CLOG(LOAD_LOG, "keeping harpoon_eventScenarioResPtr-ScenarioResStart=%d\n", ScenarioResPtr-ScenarioResStart);
      keep_harpoon_event         = alloc_HarpoonEvent((PathRecPtr)path_leg);
      *keep_harpoon_event        = harpoon_event;
      keep_harpoon_event->next   = NULL;

      if (path_leg == NULL)
      {
      CLOG(LOAD_LOG, "calling QueHarpoonEvent for this event\n");
      QueHarpoonEvent(keep_harpoon_event);
    }
    // }
  end;

  // DisposPtr(scen_har_event_ptr);
  // return TRUE;
end;

end.
