library AutoSaves; {$mode delphi}{$H+} uses Classes { you can add units after this }, uHCDefines, mainform, uHCGCI, uHCGlobals, uHCTypeDefs, uHCEventHelpers, uHCDateTime, uHCAnnexDefs, uHCAnnexHelpers, uHCUtils, uAutoSavesGlobals, Windows, Forms, ShellAPI, SysUtils, Types, Dialogs, Interfaces, Controls, DateUtils; const DLLVERN = 3; DLLVERINTN = 36; var frmMain: TfrmMain; {*** Return DLL internal version, i.e. revision 5 of my DLL ***} function DLLVer(): word; export; cdecl; begin Result := DLLVERN; end; {*** Return DLL interface version, i.e. revision 1 of my HCE interface spec ***} function DLLVerInt(): word; export; cdecl; begin Result := DLLVERINTN; end; {*** Return DLL description, 255 characters max ***} function DLLDesc(): PAnsiChar; export; cdecl; begin Result := 'AutoSaves Manager DLL by Tony Eischens'; end; {*** Free DLL ***} procedure DLLFree(); export; cdecl; begin // Do any cleanup here, this is the end frmMain.Free; end; {*** Init DLL ***} procedure DLLInit(hwndHC: HWND; pathHC: PAnsiChar; verHC: PAnsiChar; verSaveGame: longword; funcRefs: PHCDLL_FUNC_REF; PGEClosing: Pointer; PDLLBusy: Pointer; verInterface: longword; srandSeed: longword; harpoon_uuid_session: PAnsiChar; hclicense_dt_expire: Int64; hclicense_licensee: PAnsiChar; usblicense_desc: PAnsiChar; RESRSRStructureVersion: ulong; AnnexVersion: ushort); export; cdecl; var funcTmp: PHCDLL_FUNC_REF; begin // here we initialize globals and such for the life of the DLL Application.Initialize; // ALE 20120410 Freezes HC Application.Run; uAutoSavesGlobals.PGEClosing := PGEClosing; uAutoSavesGlobals.PDLLBusy := PDLLBusy; uAutoSavesGlobals.hwndHC:=hwndHC; frmMain := TfrmMain.Create(nil); // ALE 20221228 be free window! frmMain.ParentWindow := hwndHC; frmMain.Caption := 'AutoSaves Export DLL v' + IntToStr(DLLVERN) + ', ExportDLL Interface v' + IntToStr(DLLVERINTN); frmMain.memDLLInit.Lines.Add('AutoSaves Export DLL v' + IntToStr(DLLVERN)); frmMain.memDLLInit.Lines.Add('ExportDLL Interface v' + IntToStr(DLLVERINTN)); frmMain.memDLLInit.Lines.Add('hwndHC = 0x' + IntToHex(hwndHC, 8)); frmMain.memDLLInit.Lines.Add('pathHC = ' + pathHC); frmMain.memDLLInit.Lines.Add('verHC = ' + verHC); frmMain.memDLLInit.Lines.Add('verSaveGame = 0x' + IntToHex(verSaveGame, 8)); frmMain.memDLLInit.Lines.Add(Format('funcRefs = 0x%p', [funcRefs])); frmMain.memDLLInit.Lines.Add(Format('GE ExportDLL Interface Version = %d', [verInterface])); frmMain.memDLLInit.Lines.Add('RESRSRStructureVersion = ' + IntToStr(RESRSRStructureVersion)); frmMain.memDLLInit.Lines.Add('AnnexVersion = ' + IntToStr(AnnexVersion)); frmMain.memDLLInit.Lines.add('harpoon_uuid_session = ' + harpoon_uuid_session); uAutoSavesGlobals.intVerInterface := DLLVERINTN; uAutoSavesGlobals.intVerSaveGame := verSaveGame; // ALE 20170224 Alert the user that the GE is too old for this ExportDLL if (verInterface < 27) then begin MessageDlg('GE too old', GetModuleName(hInstance) + ' is designed for HC2020.005+. Please ' + ' update your GE or disable this ExportDLL.', mtWarning, [mbOK], 0); end; // ALE 20130908 List the function pointers sent over by the GE // then capture one of them for example usage funcTmp := funcRefs; while (funcTmp <> nil) do begin frmMain.memDLLInit.Lines.Add(#9 + Format('funcRef: 0x%p %s', [funcTmp^.funcPtr, funcTmp^.funcName])); if (funcTmp^.funcName = 'MessageLogLine') then frmMain.HCMessageLogLine := THCMessageLogLine(funcTmp^.funcPtr); funcTmp := funcTmp^.Next; end; end; procedure ScenLoad(strHarpPath: PAnsiChar; intScenNum: smallint; strScenFPath: PAnsiChar; intBSNum: shortint; strBSFName: PAnsiChar; intSide: byte; intGameTimeIn: Long; intGameTimeLeftIn: Long; AnnexDataIn: PHCAnnexes_disk; DatabaseVersionDBIn: PHCDATABASE_VERSION; ScenDataIn: PHCSCENARIO_DATA; ScenSaveDataIn: PHCSCENARIO_SAVE_DATA; GlobalsIn: PHCGlobals) export; cdecl; var lLLI: PHCLINEAGE_LIST_ITEM; lLineageStr: String; lRow: NativeInt; begin // ALE 20150712 Now that we receive the game time here, store it intGameTime := intGameTimeIn; // ALE 20150712 Now that we receive the game time left here, store it intGameTimeLeft := intGameTimeLeftIn; // ALE 20221227 paths for managing autosaves uAutoSavesGlobals.HCP:=strHarpPath; if (strScenFPath <> '') then begin ScenarioFNP:=strScenFPath; ScenarioFN:=ExtractFileName(strScenFPath); end else begin // ALE 20221028 built-in BattleSet scenario // sprintf(SaveName, "%3.3d-%3.3d", BattleSetNumber, ScenarioNumber); ScenarioFN:=Format('%3.3d-%3.3d', [intBSNum, intScenNum]); ScenarioFNP:=strHarpPath + '\' + ScenarioFN; end; AutoSavesP := ExtractFilePath(ScenarioFNP) + 'AutoSaves\'; frmMain.SGdedAutoSaves.Directory:=AutoSavesP; frmMain.SGdedAutoSaves.Text:=AutoSavesP; // ALE 20170923 replaced in interface version 21 with ScenData dt_scen_start := dt_scen_start_in; // ALE 20150712 Scenario Starting date and time AnnexData := AnnexDataIn; // ALE 20150813 Database/Platform information DatabaseVersionDB := DatabaseVersionDBIn; // ALE 20170923 current database ScenData := ScenDataIn; // ALE 20170923 unique identifiers, lineage, etc. ScenSaveData := ScenSaveDataIn; // ALE 20170923 save UUIDs and Scenario Orders uAutoSavesGlobals.g := GlobalsIn; if (frmMain.SLckbEnabled.Checked = True) then begin frmMain.SLmemMsgLog.Lines.Insert(0, Format('ScenLoad HC Path=%s, Scen#=%d, ScenFNP=%s, BattleSet=%d, BattleSet=%s, PlayerSide=%d, GameTime=%d, ScenStart %4.4d-%2.2d-%2.2d, %2.2d:%2.2d:%2.2d', [strHarpPath, intScenNum, strScenFPath, intBSNum, strBSFName, intSide, intGameTimeIn, ScenData^.dt_scen_start.year, ScenData^.dt_scen_start.month, ScenData^.dt_scen_start.day, ScenData^.dt_scen_start.hour, ScenData^.dt_scen_start.minute, ScenData^.dt_scen_start.second])); if (frmMain.SLmemMsgLog.Lines.Count > frmMain.SLedtLines.Value) then begin frmMain.SLmemMsgLog.Lines.Delete(frmMain.SLedtLines.Value); end; end; // ALE 20170923 Enumerate the scenario lineage if (ScenData^.Lineage.Count > 0) then begin lRow := 1; frmMain.SLsgLineage.RowCount := ScenData^.Lineage.Count + 1; frmMain.SLsgLineage.Cells[0, 0] := ''; frmMain.SLsgLineage.Cells[1, 0] := 'Date & Time Saved'; frmMain.SLsgLineage.Cells[2, 0] := 'Scen Format Ver'; frmMain.SLsgLineage.Cells[3, 0] := 'SE version'; frmMain.SLsgLineage.Cells[4, 0] := 'DB flavor'; frmMain.SLsgLineage.Cells[5, 0] := 'uuid'; lLLI := @ScenData^.Lineage.Top; while (lLLI <> nil) do begin frmMain.SLsgLineage.Cells[0, lRow] := IntToStr(lRow); frmMain.SLsgLineage.Cells[1, lRow] := FormatDateTime('yyyy-MM-dd hh:nn:ss', UnixToDateTime(lLLI^.dtstamp)); frmMain.SLsgLineage.Cells[2, lRow] := IntToStr(lLLI^.ScenarioVersion); frmMain.SLsgLineage.Cells[3, lRow] := lLLI^.SEVersion; frmMain.SLsgLineage.Cells[4, lRow] := lLLI^.DatabaseVersion.RESFile.flavor; frmMain.SLsgLineage.Cells[5, lRow] := lLLI^.uuid; Inc(lRow); lLLI := lLLI^.next; end; end; // ALE 20221228 this may be a good spot to restore the form position and state frmMain.RestoreWindowPosition(); end; procedure SaveGame(FileNameIn: PAnsiChar; ScenSaveDataIn: PHCSCENARIO_SAVE_DATA; pAutoSave: CBOOL); export; cdecl; var strDestFNP: String; blnDestSuccess: Boolean; begin if (pAutoSave = CTRUE) then begin // ALE 20221227 Move or Copy the saved game file if frmMain.SGrdbMoveCopy.ItemIndex = 0 then begin // ALE 20221228 the "Leave It" case if (frmMain.SGckbEnabled1.Checked = True) then begin frmMain.SGmemMsgLog1.Lines.Insert(0, Format('%8.8d: Game Saved: action=%s, uuid=%s, uuid_prev=%s, file=%s', [intGameTime, 'LeaveIt', ScenSaveDataIn.uuid, ScenSaveDataIn.uuid_prev, FileNameIn])); end; end else if frmMain.SGrdbMoveCopy.ItemIndex = 1 then begin // ALE 20221228 the "Move It" case ForceDirectories(AutoSavesP); strDestFNP := AutoSavesP + ExtractFileName(FileNameIn); blnDestSuccess := MoveFile(FileNameIn, PChar(strDestFNP)); if (frmMain.SGckbEnabled1.Checked = True) then begin if blnDestSuccess then begin frmMain.SGmemMsgLog1.Lines.Insert(0, Format('%8.8d: Game Saved: action=%s, uuid=%s, uuid_prev=%s, file=%s', [intGameTime, 'MoveIt_Success', ScenSaveDataIn.uuid, ScenSaveDataIn.uuid_prev, strDestFNP])); end else begin frmMain.SGmemMsgLog1.Lines.Insert(0, Format('%8.8d: Game Saved: action=%s, uuid=%s, uuid_prev=%s, file=%s', [intGameTime, 'MoveIt_Failed', ScenSaveDataIn.uuid, ScenSaveDataIn.uuid_prev, FileNameIn])); end; end; end else if frmMain.SGrdbMoveCopy.ItemIndex = 2 then begin // ALE 20221228 the "Copy It" case ForceDirectories(AutoSavesP); strDestFNP := AutoSavesP + ExtractFileName(FileNameIn); blnDestSuccess := CopyFile(FileNameIn, PChar(strDestFNP), False); if (frmMain.SGckbEnabled1.Checked = True) then begin if blnDestSuccess then begin frmMain.SGmemMsgLog1.Lines.Insert(0, Format('%8.8d: Game Saved: action=%s, uuid=%s, uuid_prev=%s, file=%s', [intGameTime, 'CopyIt_Success', ScenSaveDataIn.uuid, ScenSaveDataIn.uuid_prev, strDestFNP])); end else begin frmMain.SGmemMsgLog1.Lines.Insert(0, Format('%8.8d: Game Saved: action=%s, uuid=%s, uuid_prev=%s, file=%s', [intGameTime, 'CopyIt_Failed', ScenSaveDataIn.uuid, ScenSaveDataIn.uuid_prev, FileNameIn])); end; end; end; end else begin frmMain.SGmemMsgLog1.Lines.Insert(0, Format('%8.8d: Game Saved: uuid=%s, uuid_prev=%s, file=%s', [intGameTime, ScenSaveDataIn.uuid, ScenSaveDataIn.uuid_prev, FileNameIn])); end; if (frmMain.SGckbEnabled1.Checked = True) then begin if (frmMain.SGmemMsgLog1.Lines.Count > frmMain.SGedtLines1.Value) then begin frmMain.SGmemMsgLog1.Lines.Delete(frmMain.SGedtLines1.Value); end; end; end; procedure GameTime(intGameTimeIn: Long; intGameTimeLeftIn: Long); export; cdecl; begin // ALE 20150712 Now that we receive the game time here, store it intGameTime := intGameTimeIn; // ALE 20150712 Now that we receive the game time left here, store it intGameTimeLeft := intGameTimeLeftIn; end; exports DLLVer, DLLVerInt, DLLDesc, DLLFree, DLLInit, ScenLoad , SaveGame // ALE 20170923 UUIDs to associate any data with the right saved game file , GameTime ; end.