563 lines
15 KiB
ObjectPascal
563 lines
15 KiB
ObjectPascal
// ====================================================================
|
||
// Mystic BBS Software Copyright 1997-2012 By James Coyle
|
||
// ====================================================================
|
||
//
|
||
// This file is part of Mystic BBS.
|
||
//
|
||
// Mystic BBS is free software: you can redistribute it and/or modify
|
||
// it under the terms of the GNU General Public License as published by
|
||
// the Free Software Foundation, either version 3 of the License, or
|
||
// (at your option) any later version.
|
||
//
|
||
// Mystic BBS is distributed in the hope that it will be useful,
|
||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
// GNU General Public License for more details.
|
||
//
|
||
// You should have received a copy of the GNU General Public License
|
||
// along with Mystic BBS. If not, see <http://www.gnu.org/licenses/>.
|
||
//
|
||
// ====================================================================
|
||
|
||
Program MystPack;
|
||
|
||
{ when DELETEing a message, the pointers are one less than they should be }
|
||
{ should be fixed, but may cause other problems. commented out the last }
|
||
{ read updating in the msgkill part of the program }
|
||
|
||
{ also when a user is reading a base, it could cause MP to crash with an }
|
||
{ RTE 005: access violation error }
|
||
|
||
{$I M_OPS.PAS}
|
||
|
||
Uses
|
||
m_FileIO,
|
||
m_Strings,
|
||
m_DateTime,
|
||
CRT,
|
||
DOS;
|
||
|
||
{$I RECORDS.PAS}
|
||
|
||
Const
|
||
PackVer = '1.2';
|
||
Jam_Deleted = $80000000;
|
||
JamSubBufSize = 4096;
|
||
|
||
Type
|
||
JamSubBuffer = Array[1..JamSubBufSize] of Char;
|
||
|
||
JamHdrType = Record
|
||
Signature : Array[1..4] of Char;
|
||
Created : LongInt;
|
||
ModCounter : LongInt;
|
||
ActiveMsgs : LongInt;
|
||
PwdCRC : LongInt;
|
||
BaseMsgNum : LongInt;
|
||
HighWaterMark : Longint;
|
||
Extra : Array[1..996] of Char;
|
||
End;
|
||
|
||
JamMsgHdrType = Record
|
||
Signature : Array[1..4] of Char;
|
||
Rev : Word;
|
||
Resvd : Word;
|
||
SubFieldLen : LongInt;
|
||
TimesRead : LongInt;
|
||
MsgIdCrc : LongInt;
|
||
ReplyCrc : LongInt;
|
||
ReplyTo : LongInt;
|
||
ReplyFirst : LongInt;
|
||
ReplyNext : LongInt;
|
||
DateWritten : LongInt;
|
||
DateRcvd : LongInt;
|
||
DateArrived : LongInt;
|
||
MsgNumber : LongInt;
|
||
Attr1 : LongInt;
|
||
Attr2 : LongInt;
|
||
TextOfs : LongInt;
|
||
TextLen : LongInt;
|
||
PwdCrc : LongInt;
|
||
Cost : LongInt;
|
||
End;
|
||
|
||
JamIdxType = Record
|
||
MsgToCrc : LongInt;
|
||
HdrLoc : LongInt;
|
||
End;
|
||
|
||
JamLastType = Record
|
||
NameCrc : LongInt;
|
||
UserNum : LongInt;
|
||
LastRead : LongInt;
|
||
HighRead : LongInt;
|
||
End;
|
||
|
||
SubFieldType = Record
|
||
LoId : Word;
|
||
HiId : Word;
|
||
DataLen : LongInt;
|
||
Data : Array[1..1000] of Char;
|
||
End;
|
||
|
||
TxtType = Array[1..65000] of Char;
|
||
|
||
JamType = Record
|
||
Hdr : JamHdrType;
|
||
MsgHdr : JamMsgHdrType;
|
||
HdrFile : File;
|
||
Idx : JamIdxType;
|
||
IdxFile : File of JamIdxType;
|
||
Last : JamLastType;
|
||
LastFile : File of JamLastType;
|
||
TxtFile : File;
|
||
SubField : SubFieldType;
|
||
End;
|
||
|
||
Const
|
||
SpinStr : String[8] = ('\|/-\|/-');
|
||
SpinPos : Byte = 1;
|
||
SkipFirst : Boolean = False;
|
||
PackMsgs : Boolean = False;
|
||
|
||
Var
|
||
ConfigFile : File of RecConfig;
|
||
MBaseFile : File of RecMessageBase;
|
||
Config : RecConfig;
|
||
MBase : RecMessageBase;
|
||
|
||
Const
|
||
DATEC1970 = 2440588;
|
||
DATED0 = 1461;
|
||
DATED1 = 146097;
|
||
DATED2 = 1721119;
|
||
|
||
Procedure UnixToDT(SecsPast: LongInt; Var Dt: DateTime);
|
||
Var
|
||
DateNum : LongInt;
|
||
N1 : Word;
|
||
Begin
|
||
Datenum := (SecsPast Div 86400) + DATEc1970;
|
||
|
||
DateJ2G(DateNum, SmallInt(N1), SmallInt(DT.Month), SmallInt(DT.day));
|
||
DT.Year := N1;
|
||
|
||
SecsPast := SecsPast Mod 86400;
|
||
DT.Hour := SecsPast Div 3600;
|
||
SecsPast := SecsPast Mod 3600;
|
||
DT.Min := SecsPast Div 60;
|
||
DT.Sec := SecsPast Mod 60;
|
||
End;
|
||
|
||
Procedure PWrite (Str : String);
|
||
Var
|
||
A : Byte;
|
||
Code : String[2];
|
||
Begin
|
||
A := 1;
|
||
While A <= Length(Str) Do Begin
|
||
If (Str[A] = '|') and (A < Length(Str) - 1) Then Begin
|
||
Code := Copy(Str, A + 1, 2);
|
||
|
||
If (Code = '00') or (strS2I(Code) > 0) Then Begin
|
||
If strS2I(Code) < 16 Then
|
||
TextColor(strS2I(Code))
|
||
Else
|
||
TextBackground(strS2I(Code) - 16);
|
||
End Else
|
||
Write(Str[A] + Code);
|
||
|
||
Inc (A, 2);
|
||
End Else
|
||
Write(Str[A]);
|
||
|
||
Inc(A);
|
||
End;
|
||
End;
|
||
|
||
Procedure PWriteLN (Str : String);
|
||
Begin
|
||
PWrite (Str + #13#10);
|
||
End;
|
||
|
||
Procedure UpdateSpin;
|
||
Begin
|
||
Write (#8 + SpinStr[SpinPos]);
|
||
Inc (SpinPos);
|
||
|
||
If SpinPos > 8 Then SpinPos := 1;
|
||
End;
|
||
|
||
Procedure PackJAMBase (Var TotalKilled : LongInt; Var SavedBytes : LongInt);
|
||
Var
|
||
BasePath : String;
|
||
OldHdrFile : File;
|
||
OldTxtFile : File;
|
||
OldIdxFile : File of JamIdxType;
|
||
NewHdrFile : File;
|
||
NewTxtFile : File;
|
||
NewIdxFile : File of JamIdxType;
|
||
TmpHdrFile : File;
|
||
LastFile : File of JamLastType;
|
||
Last : JamLastType;
|
||
SigHdr : JamHdrType;
|
||
MsgHdr : JamMsgHdrType;
|
||
TmpSigHdr : JamHdrType;
|
||
TmpMsgHdr : JamMsgHdrType;
|
||
MsgIdx : JamIdxType;
|
||
TxtBuf : ^TxtType;
|
||
SubField : SubFieldType;
|
||
Count : LongInt;
|
||
Killed : Boolean;
|
||
KillOffset : LongInt;
|
||
LimitKill : Boolean;
|
||
TotalMsgs : LongInt;
|
||
MsgDateTime : DateTime;
|
||
Temp : LongInt;
|
||
HaveHdr : Boolean;
|
||
Begin
|
||
PWrite ('|07Processing |08-> |07' + strPadR(MBase.Name, 35, ' ') + '|08 -> |07');
|
||
|
||
BasePath := MBase.Path + MBase.FileName;
|
||
|
||
Assign (OldHdrFile, BasePath + '.jhr');
|
||
Assign (OldTxtFile, BasePath + '.jdt');
|
||
Assign (OldIdxFile, BasePath + '.jdx');
|
||
|
||
{$I-} Reset (OldHdrFile, 1); {$I+}
|
||
If IOResult <> 0 Then Exit;
|
||
|
||
{$I-} Reset (OldTxtFile, 1); {$I+}
|
||
If IOResult <> 0 Then Begin
|
||
Close (OldHdrFile);
|
||
Exit;
|
||
End;
|
||
|
||
{$I-} Reset (OldIdxFile); {$I+}
|
||
If IoResult <> 0 Then Begin
|
||
Close (OldHdrFile);
|
||
Close (OldTxtFile);
|
||
Exit;
|
||
End;
|
||
|
||
Assign (LastFile, BasePath + '.jlr');
|
||
{$I-} Reset (LastFile); {$I+}
|
||
If IoResult <> 0 Then ReWrite (LastFile);
|
||
Close (LastFile);
|
||
|
||
Assign (NewHdrFile, BasePath + '._hr');
|
||
ReWrite (NewHdrFile, 1);
|
||
Assign (NewTxtFile, BasePath + '._dt');
|
||
ReWrite (NewTxtFile, 1);
|
||
Assign (NewIdxFile, BasePath + '._dx');
|
||
ReWrite (NewIdxFile);
|
||
|
||
BlockRead (OldHdrFile, SigHdr, SizeOf(SigHdr));
|
||
|
||
Inc (SigHdr.ModCounter);
|
||
|
||
BlockWrite (NewHdrFile, SigHdr, SizeOf(SigHdr));
|
||
|
||
If SigHdr.ActiveMsgs > MBase.MaxMsgs Then
|
||
KillOffset := SigHdr.ActiveMsgs - MBase.MaxMsgs
|
||
Else
|
||
KillOffset := 0;
|
||
|
||
TotalMsgs := 0;
|
||
TotalKilled := 0;
|
||
|
||
New (TxtBuf);
|
||
|
||
While Not Eof(OldIdxFile) Do Begin
|
||
UpdateSpin;
|
||
|
||
Read (OldIdxFile, MsgIdx);
|
||
|
||
If MsgIdx.HdrLoc = -1 Then Begin
|
||
Killed := True;
|
||
LimitKill := False;
|
||
HaveHdr := False;
|
||
End Else Begin
|
||
Seek (OldHdrFile, MsgIdx.HdrLoc);
|
||
|
||
BlockRead (OldHdrFile, MsgHdr, SizeOf(MsgHdr));
|
||
|
||
LimitKill := False;
|
||
Killed := MsgHdr.Attr1 and Jam_Deleted <> 0;
|
||
HaveHdr := True;
|
||
|
||
If MBase.MaxAge > 0 Then Begin
|
||
UnixToDT (MsgHdr.DateWritten, MsgDateTime);
|
||
PackTime (MsgDateTime, Temp);
|
||
|
||
LimitKill := DaysAgo(Temp, 2) > MBase.MaxAge;
|
||
Killed := Killed or LimitKill;
|
||
End;
|
||
|
||
If MBase.MaxMsgs > 0 Then
|
||
If KillOffset > 0 Then Begin
|
||
Dec (KillOffset);
|
||
LimitKill := True;
|
||
Killed := True;
|
||
End;
|
||
|
||
If SkipFirst and (MBase.NetType = 0) and (TotalMsgs = 0) and (MsgHdr.Attr1 and Jam_Deleted = 0) Then
|
||
Killed := False;
|
||
End;
|
||
|
||
If Killed Then Begin
|
||
Inc (TotalKilled);
|
||
|
||
(*
|
||
Reset (LastFile);
|
||
While Not Eof(LastFile) Do Begin
|
||
Read (LastFile, Last);
|
||
If (Last.LastRead > TotalMsgs) And Not LimitKill Then Begin
|
||
Dec (Last.LastRead);
|
||
Seek (LastFile, FilePos(LastFile) - 1);
|
||
Write (LastFile, Last);
|
||
End;
|
||
End;
|
||
Close (LastFile);
|
||
*)
|
||
If HaveHdr And (MsgHdr.ReplyFirst <> 0) Then Begin
|
||
Assign (TmpHdrFile, BasePath + '.jhr');
|
||
Reset (TmpHdrFile, 1);
|
||
|
||
BlockRead (TmpHdrFile, TmpSigHdr, SizeOf(TmpSigHdr));
|
||
|
||
While Not Eof(TmpHdrFile) Do Begin
|
||
BlockRead (TmpHdrFile, TmpMsgHdr, SizeOf(TmpMsgHdr));
|
||
|
||
If TmpMsgHdr.ReplyTo = MsgHdr.MsgNumber Then Begin
|
||
TmpMsgHdr.ReplyTo := 0;
|
||
Seek (TmpHdrFile, FilePos(TmpHdrFile) - SizeOf(TmpMsgHdr));
|
||
BlockWrite (TmpHdrFile, TmpMsgHdr, SizeOf(TmpMsgHdr));
|
||
End;
|
||
|
||
Seek (TmpHdrFile, FilePos(TmpHdrFile) + TmpMsgHdr.SubFieldLen);
|
||
End;
|
||
Close (TmpHdrFile);
|
||
End;
|
||
|
||
End Else Begin
|
||
Inc (TotalMsgs);
|
||
|
||
If TotalKilled > 0 Then Begin
|
||
Reset (LastFile);
|
||
While Not Eof(LastFile) Do Begin
|
||
Read (LastFile, Last);
|
||
If Last.LastRead = MsgHdr.MsgNumber Then Begin
|
||
Last.LastRead := TotalMsgs;
|
||
Seek (LastFile, FilePos(LastFile) - 1);
|
||
Write (LastFile, Last);
|
||
End;
|
||
End;
|
||
Close (LastFile);
|
||
End;
|
||
|
||
If (TotalKilled > 0) and (MsgHdr.ReplyFirst <> 0) Then Begin
|
||
Assign (TmpHdrFile, BasePath + '.jhr');
|
||
Reset (TmpHdrFile, 1);
|
||
|
||
BlockRead (TmpHdrFile, TmpSigHdr, SizeOf(TmpSigHdr));
|
||
|
||
While Not Eof(TmpHdrFile) Do Begin
|
||
BlockRead (TmpHdrFile, TmpMsgHdr, SizeOf(TmpMsgHdr));
|
||
|
||
If TmpMsgHdr.ReplyTo = MsgHdr.MsgNumber Then Begin
|
||
TmpMsgHdr.ReplyTo := TotalMsgs;
|
||
Seek (TmpHdrFile, FilePos(TmpHdrFile) - SizeOf(TmpMsgHdr));
|
||
BlockWrite (TmpHdrFile, TmpMsgHdr, SizeOf(TmpMsgHdr));
|
||
End;
|
||
|
||
Seek (TmpHdrFile, FilePos(TmpHdrFile) + TmpMsgHdr.SubFieldLen);
|
||
End;
|
||
Close (TmpHdrFile);
|
||
End;
|
||
|
||
If (TotalKilled > 0) and (MsgHdr.ReplyTo <> 0) Then Begin
|
||
Assign (TmpHdrFile, BasePath + '._hr');
|
||
Reset (TmpHdrFile, 1);
|
||
|
||
BlockRead (TmpHdrFile, TmpSigHdr, SizeOf(TmpSigHdr));
|
||
|
||
While Not Eof(TmpHdrFile) Do Begin
|
||
BlockRead (TmpHdrFile, TmpMsgHdr, SizeOf(TmpMsgHdr));
|
||
|
||
If TmpMsgHdr.ReplyFirst = MsgHdr.MsgNumber Then Begin
|
||
TmpMsgHdr.ReplyFirst := TotalMsgs;
|
||
Seek (TmpHdrFile, FilePos(TmpHdrFile) - SizeOf(TmpMsgHdr));
|
||
BlockWrite (TmpHdrFile, TmpMsgHdr, SizeOf(TmpMsgHdr));
|
||
End;
|
||
|
||
Seek (TmpHdrFile, FilePos(TmpHdrFile) + TmpMsgHdr.SubFieldLen);
|
||
End;
|
||
Close (TmpHdrFile);
|
||
End;
|
||
|
||
MsgHdr.MsgNumber := TotalMsgs;
|
||
MsgIdx.HdrLoc := FilePos(NewHdrFile);
|
||
|
||
(* write text from old file to new file *)
|
||
|
||
If MsgHdr.TextLen > 65000 Then MsgHdr.TextLen := 65000;
|
||
// Why did I put this limitation here? Prob should be removed
|
||
// need to be tested.
|
||
|
||
Seek (OldTxtFile, MsgHdr.TextOfs);
|
||
BlockRead (OldTxtFile, TxtBuf^, MsgHdr.TextLen);
|
||
|
||
MsgHdr.TextOfs := FileSize(NewTxtFile);
|
||
|
||
BlockWrite (NewTxtFile, TxtBuf^, MsgHdr.TextLen);
|
||
|
||
(* write header from old to new file *)
|
||
|
||
BlockWrite (NewHdrFile, MsgHdr, SizeOf(MsgHdr));
|
||
|
||
(* write subfield data if it exists *)
|
||
|
||
If MsgHdr.SubFieldLen > 0 Then Begin
|
||
Count := 1;
|
||
|
||
While (Count <= MsgHdr.SubFieldLen) Do Begin
|
||
BlockRead (OldHdrFile, SubField, 8);
|
||
BlockRead (OldHdrFile, SubField.Data, SubField.DataLen);
|
||
BlockWrite (NewHdrFile, SubField, 8);
|
||
BlockWrite (NewHdrFile, SubField.Data, SubField.DataLen);
|
||
|
||
Inc (Count, 8 + SubField.DataLen);
|
||
End;
|
||
End;
|
||
|
||
(* write new index to index file *)
|
||
|
||
Write (NewIdxFile, MsgIdx);
|
||
End;
|
||
End;
|
||
|
||
Dispose (TxtBuf);
|
||
|
||
SigHdr.ActiveMsgs := TotalMsgs;
|
||
SigHdr.BaseMsgNum := 1;
|
||
|
||
Reset (NewHdrFile, 1);
|
||
BlockWrite (NewHdrFile, SigHdr, SizeOf(SigHdr));
|
||
|
||
SavedBytes := (FileSize(OldHdrFile) - FileSize(NewHdrFile)) +
|
||
(FileSize(OldTxtFile) - FileSize(NewTxtFile)) +
|
||
((FileSize(OldIdxFile) - FileSize(NewIdxFile)) * SizeOf(MsgIdx));
|
||
|
||
Close (OldHdrFile);
|
||
Close (OldTxtFile);
|
||
Close (OldIdxFile);
|
||
Close (NewHdrFile);
|
||
Close (NewTxtFile);
|
||
Close (NewIdxFile);
|
||
|
||
Erase (OldHdrFile);
|
||
Erase (OldTxtFile);
|
||
Erase (OldIdxFile);
|
||
|
||
ReName (NewHdrFile, BasePath + '.jhr');
|
||
ReName (NewTxtFile, BasePath + '.jdt');
|
||
ReName (NewIdxFile, BasePath + '.jdx');
|
||
|
||
If TotalKilled > 0 Then Begin
|
||
Reset (LastFile);
|
||
While Not Eof(LastFile) Do Begin
|
||
Read (LastFile, Last);
|
||
If Last.LastRead > TotalMsgs Then Last.LastRead := TotalMsgs;
|
||
If Last.HighRead > Last.LastRead Then Last.HighRead := Last.LastRead;
|
||
Seek (LastFile, FilePos(LastFile) - 1);
|
||
Write (LastFile, Last);
|
||
End;
|
||
Close (LastFile);
|
||
End;
|
||
End;
|
||
|
||
Procedure ShowHelp;
|
||
Begin
|
||
WriteLn ('Invalid command line options');
|
||
WriteLn;
|
||
WriteLn ('-PACK : Pack all jam message bases');
|
||
WriteLn ('-SKIPFIRST : Skips the first message of each local message base');
|
||
WriteLn;
|
||
PWriteLn ('|12NOTE: This program can sometimes crash if users are online.|07');
|
||
Halt(1);
|
||
End;
|
||
|
||
Var
|
||
TotalMsgs : LongInt;
|
||
TotalBytes : LongInt;
|
||
Msgs : LongInt;
|
||
Bytes : LongInt;
|
||
Count : Byte;
|
||
Str : String;
|
||
Begin
|
||
FileMode := 66;
|
||
|
||
ClrScr;
|
||
PWriteLn ('|08-> |15MYSTPACK ' + PackVer + ' : JAM message base packer');
|
||
PWriteLn ('|08-> |07Compatible with Mystic BBS software v' + mysVersion);
|
||
PWriteLn ('|08<30><38><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>|07');
|
||
WriteLn;
|
||
|
||
Window (1, 5, 80, 24);
|
||
|
||
If ParamCount = 0 Then ShowHelp;
|
||
|
||
For Count := 1 to ParamCount Do Begin
|
||
Str := strUpper(ParamStr(Count));
|
||
|
||
If Str = '-PACK' Then
|
||
PackMsgs := True
|
||
Else
|
||
If Str = '-SKIPFIRST' Then
|
||
SkipFirst := True
|
||
Else
|
||
ShowHelp;
|
||
End;
|
||
|
||
Assign (ConfigFile, 'mystic.dat');
|
||
{$I-} Reset (ConfigFile); {$I+}
|
||
If IoResult <> 0 Then Begin
|
||
WriteLn ('ERROR: Unable to read MYSTIC.DAT. Run from root Mystic directory');
|
||
Halt(1);
|
||
End;
|
||
Read (ConfigFile, Config);
|
||
Close (ConfigFile);
|
||
|
||
If Config.DataChanged <> mysDataChanged Then Begin
|
||
WriteLn('ERROR: Data files are not current and must be upgraded.');
|
||
Halt(1);
|
||
End;
|
||
|
||
Assign (MBaseFile, Config.DataPath + 'mbases.dat');
|
||
{$I-} Reset(MBaseFile); {$I+}
|
||
If IoResult <> 0 Then Begin
|
||
WriteLn ('ERROR: Unable to read message area data');
|
||
Halt(1);
|
||
End;
|
||
|
||
While Not Eof(MBaseFile) Do Begin
|
||
Read (MBaseFile, MBase);
|
||
|
||
If MBase.BaseType = 0 Then Begin
|
||
PackJAMBase(Msgs, Bytes);
|
||
WriteLn (#8 + 'Killed ', Msgs, '; ', Bytes, ' bytes');
|
||
|
||
Inc (TotalMsgs, Msgs);
|
||
Inc (TotalBytes, Bytes);
|
||
End;
|
||
End;
|
||
|
||
Close (MBaseFile);
|
||
|
||
WriteLn;
|
||
PWriteLn ('|08[|07-|08] |07Killed |15' + strI2S(TotalMsgs) + '|07 Msgs; Removed |15' + strI2S(TotalBytes) + '|07 bytes');
|
||
|
||
Window (1, 1, 80, 25);
|
||
End.
|