12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343 |
- {
- Copyright (C) 2005 Remco Mulder
- This program 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 2 of the License, or
- (at your option) any later version.
- This program 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 this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- For source notes please refer to Notes.txt
- For license terms please refer to GPL.txt.
- These files should be stored in the root of the compression you
- received this source in.
- }
- // This unit controls all database access.
- unit
- DataBase;
- interface
- uses
- Core,
- Classes,
- SysUtils;
- const
- DATABASE_VERSION = 7;
- Day : array[1..7] of string = ('Sun', 'Mon', 'Tues', 'Wed', 'Thurs', 'Fri', 'Sat');
- type
- // Enumerated types
- TFighterType = (ftToll, ftDefensive, ftOffensive);
- TSectorExploredType = (etNo, etCalc, etDensity, etHolo);
- TProductType = (ptFuelOre, ptOrganics, ptEquipment);
- // Exceptions
- EDatabaseError = class(Exception);
- // database records
- TDataHeader = record
- ProgramName : string[12];
- Version : Byte;
- Sectors,
- StarDock,
- Class0_1,
- Class0_2 : Word;
- Address,
- Description : string[40];
- Port : Word;
- LoginScript : string[255];
- Password,
- LoginName : string[40];
- Game : Char;
- UseLogin : Boolean;
- RobFactor,
- StealFactor : Byte;
- LastPortCIM : TDateTime;
- end;
- PDataHeader = ^TDataHeader;
- TSpaceObject = record
- Quantity : LongInt;
- Owner : string[40];
- FigType : TFighterType;
- end;
- PTrader = ^TTrader;
- TTrader = record
- Name,
- ShipType,
- ShipName : string[40];
- Figs : LongInt;
- NextTrader : LongInt;
- end;
- PShip = ^TShip;
- TShip = record
- Name,
- Owner,
- ShipType : string[40];
- Figs : LongInt;
- NextShip : LongInt;
- end;
- TPort = record
- Name : string[40];
- Dead : Boolean;
- BuildTime,
- ClassIndex : Byte;
- BuyProduct : array[TProductType] of Boolean;
- ProductPercent : array[TProductType] of Byte;
- ProductAmount : array[TProductType] of Word;
- UpDate : TDateTime;
- end;
- PSector = ^TSector;
- TSector = record
- // Index : Word;
- Warp : array[1..6] of Word;
- SPort : TPort;
- NavHaz : Byte;
- Figs,
- Mines_Armid,
- Mines_Limpet : TSpaceObject;
- Constellation,
- Beacon : string[40];
- UpDate : TDateTime;
- Anomoly : Boolean;
- Density : LongInt;
- Warps : Byte;
- Explored : TSectorExploredType;
- Ships,
- Traders,
- Planets,
- Vars : LongInt;
- end;
- PPlanet = ^TPlanet;
- TPlanet = record
- Name : string[40];
- NextPlanet : LongInt;
- end;
- PWarpIn = ^TWarpIn;
- TWarpIn = record
- Origin : Word;
- NextWarpIn : PWarpIn;
- end;
- TSectorVar = record
- VarName: string[10];
- Value: string[40];
- NextVar: LongInt;
- end;
- PSectorVar = ^TSectorVar;
- TSectorItem = (itPlanet, itTrader, itShip);
- TEmptyRecordGroup = record
- RecordSize : Word;
- EmptyRecords : array of Integer;
- end;
- TModDatabase = class(TTWXModule, IModDatabase, ITWXGlobals)
- private
- CacheAllocated,
- FRecording,
- FUseCache,
- FDataBaseOpen : Boolean;
- FDataFilename : string;
- DataFile : File;
- DataCache,
- SectorWarpCache : Pointer;
- DBSize,
- CacheSize : Integer;
- EmptyRecordGroups : array of TEmptyRecordGroup;
- FDBHeader : TDataHeader;
- FProgramDir : string;
- procedure AddSectorVar(SectorIndex: Integer; SectorVar: PSectorVar);
- procedure FindSectorVar(SectorIndex: Integer; const VarName: string; var SectorVar: PSectorVar; var Index: Integer);
- procedure DeleteSectorVar(SectorIndex: Integer; const VarName: string);
- procedure AddWarpIn(Sect, Origin : Word);
- function GetEmptyRecord(Size : Integer) : Integer;
- procedure CacheEmptyRecords;
- procedure CacheEmptyRecord(Index : Integer; Size : Word);
- procedure ReadRecordList(List : TList; FirstPos : Integer);
- function WriteRecordList(List : TList; RecordSize : Word) : Integer;
- procedure PurgeRecordList(FirstPos : Integer);
- procedure WriteRecord(Rec : Pointer; Pos, Next : Integer; Size : Word);
- procedure ReadData(Data : Pointer; Index, Size : Integer);
- procedure WriteData(Data : Pointer; Index, Size : Integer);
- procedure OpenCache;
- procedure CloseCache;
- function GetLastPortCIM: TDateTime;
- procedure SetLastPortCIM(const Value: TDateTime);
- protected
- { ITWXGlobals }
- function GetProgramDir: string;
- procedure SetProgramDir(const Value: string);
- { IModDatabase }
- function GetDatabaseName: string;
- procedure SetDatabaseName(const Value: string);
- function GetUseCache: Boolean;
- procedure SetUseCache(Value: Boolean);
- function GetRecording: Boolean;
- procedure SetRecording(Value: Boolean);
- public
- procedure AfterConstruction; override;
- procedure BeforeDestruction; override;
- // DB control methods
- procedure OpenDataBase(Filename : string);
- procedure CloseDataBase;
- procedure CreateDatabase(Filename : string; Head : TDataHeader);
- // main storage/retrieval methods
- procedure SaveSector(S: TSector; Index: Integer; ShipList: TList = nil; TraderList: TList = nil; PlanetList: TList = nil);
- function LoadSector(I : Integer) : TSector;
- procedure UpdateWarps(SectIndex : Integer);
- function GetSectorItems(ItemType : TSectorItem; Sector : TSector) : TList;
- function GetWarpsIn(Sect : Integer) : TList;
- function GetBackDoors(S: TSector; SectorIndex: Integer) : TList;
- function PlotWarpCourse(FromSect, ToSect : Word) : TList;
- procedure WriteHeader;
- procedure DumpData;
- procedure SetSectorVar(SectorIndex: Integer; const VarName, VarValue: string);
- function GetSectorVar(SectorIndex: Integer; const VarName: string): string;
- // null item retrieval
- procedure NULLSector(var Sector : TSector);
- procedure NULLPlanet(var Planet : TPlanet);
- procedure NULLTrader(var Trader : TTrader);
- procedure NULLShip(var Ship : TShip);
- property Sectors[Index : Integer] : TSector read LoadSector;
- property DataBaseOpen : Boolean read FDataBaseOpen;
- property DBHeader: TDataHeader read FDBHeader;
- property LastPortCIM: TDateTime read GetLastPortCIM write SetLastPortCIM;
- published
- property DatabaseName: string read GetDatabaseName write SetDatabaseName;
- property UseCache: Boolean read GetUseCache write SetUseCache;
- property Recording: Boolean read GetRecording write SetRecording;
- end;
- function GetBlankHeader : PDataHeader;
- implementation
- uses
- Global,
- Utility,
- Windows,
- Forms,
- Ansi,
- Dialogs;
- type
- TWarpItem = class(TObject)
- public
- Index : Word;
- Parent : TWarpItem;
- end;
- function WarpsTo(Source: TSector; DestIndex: Integer) : Boolean;
- begin
- WarpsTo := (Source.Warp[1] = DestIndex)
- or (Source.Warp[2] = DestIndex)
- or (Source.Warp[3] = DestIndex)
- or (Source.Warp[4] = DestIndex)
- or (Source.Warp[5] = DestIndex)
- or (Source.Warp[6] = DestIndex);
- end;
- function GetBlankHeader : PDataHeader;
- begin
- Result := AllocMem(SizeOf(TDataHeader));
- ZeroMemory(Result, SizeOf(TDataHeader));
- Result^.ProgramName := 'TWX DATABASE';
- Result^.Version := DATABASE_VERSION;
- Result^.Address := '<Server>';
- Result^.Port := 23;
- end;
- // ***********************************************************
- // Public Implementation
- procedure TModDatabase.AfterConstruction;
- begin
- inherited;
- // initialise variables
- FDatabaseOpen := FALSE;
- SectorWarpCache := nil;
- FUseCache := True;
- FRecording := True;
- end;
- procedure TModDatabase.BeforeDestruction;
- begin
- // ensure database is closed
- CloseDataBase;
- inherited;
- end;
- function TModDatabase.GetProgramDir: string;
- begin
- Result := FProgramDir;
- end;
- procedure TModDatabase.SetProgramDir(const Value: string);
- begin
- FProgramDir := Value;
- end;
- // ---------------------------
- // DB control methods
- procedure TModDatabase.OpenDataBase(Filename : string);
- var
- WarpCount,
- I,
- X : Integer;
- S : TSector;
- begin
- if (DataBaseOpen) then
- Exit;
- TWXServer.Broadcast(endl + ANSI_15 + 'Loading database: ' + ANSI_7 + Filename + endl);
- FDataFilename := Filename;
- SetCurrentDir(FProgramDir);
- // Open database
- AssignFile(DataFile, Filename);
- {$I-}
- Reset(DataFile, 1);
- if (IOResult <> 0) then
- begin
- TWXServer.Broadcast(endl + ANSI_12 + 'Warning: This database does not exist. No data will be saved/retrieved' + ANSI_7 + endl);
- CloseDatabase;
- Exit;
- end;
- {$I+}
- // Check database validity and version number
- Seek(DataFile, 0);
- BlockRead(DataFile, FDBHeader, SizeOf(TDataHeader));
- if (DBHeader.ProgramName <> 'TWX DATABASE') then
- begin
- TWXServer.Broadcast(endl + ANSI_12 + 'Warning: This database has been corrupted, no data will be saved/retrieved' + ANSI_7 + endl);
- CloseDatabase;
- Exit;
- end;
- if (DBHeader.Version <> DATABASE_VERSION) then
- begin
- TWXServer.Broadcast(endl + ANSI_12 + 'Warning: Database version ' + IntToStr(DBHeader.Version) + ', expected version ' + IntToStr(DATABASE_VERSION) + ', no data will be saved/retrieved' + ANSI_7 + endl);
- CloseDatabase;
- Exit;
- end;
- if (UseCache) then
- OpenCache
- else
- CloseCache;
- FDatabaseOpen := TRUE;
- DBSize := FileSize(DataFile);
- // cache empty record indexes
- CacheEmptyRecords;
- // construct sector warp cache
- SectorWarpCache := AllocMem(DBHeader.Sectors * 4);
- WarpCount := 0;
- for I := 1 to DBHeader.Sectors do
- begin
- S := LoadSector(I);
- for X := 1 to 6 do
- if (S.Warp[X] > 0) then
- begin
- Inc(WarpCount);
- AddWarpIn(S.Warp[X], I);
- end
- else
- Break;
- end;
- TWXLog.DatabaseChanged;
- TWXServer.Broadcast(endl + ANSI_15 + 'Database successfully loaded - ' + IntToStr(DBHeader.Sectors) + ' sectors, ' + IntToStr(WarpCount) + ' warps' + endl);
- TWXGUI.DatabaseName := StripFileExtension(ShortFilename(Filename));
- end;
- procedure TModDatabase.CloseDataBase;
- var
- W,
- Last : PWarpIn;
- P : Pointer;
- I,
- CacheEnd : Integer;
- begin
- if not (FDatabaseOpen) then
- Exit;
- {$I-}
- CloseFile(DataFile);
- {$I+}
- FDatabaseOpen := FALSE;
- // deconstruct sector warp cache
- P := SectorWarpCache;
- CacheEnd := Integer(P) + DBHeader.Sectors * 4;
- while (Integer(P) < CacheEnd) do
- begin
- W := PWarpIn(P^);
- while (W <> nil) do
- begin
- Last := W;
- W := W^.NextWarpIn;
- FreeMem(Last);
- end;
- P := Pointer(Integer(P) + SizeOf(Pointer));
- end;
- // purge old empty record caches
- if (Length(EmptyRecordGroups) > 0) then
- for I := 0 to Length(EmptyRecordGroups) - 1 do
- SetLength(EmptyRecordGroups[I].EmptyRecords, 0);
- SetLength(EmptyRecordGroups, 0);
- FreeMem(SectorWarpCache);
- CloseCache;
- FDataFilename := '';
- end;
- procedure TModDatabase.CreateDatabase(Filename : string; Head : TDataHeader);
- var
- I : Integer;
- Sect : TSector;
- F : File;
- FileOpen : Boolean;
- begin
- // Make a database - it doesn't exist
- TWXServer.Broadcast(endl + ANSI_15 + 'Creating database: ' + ANSI_7 + Filename + ANSI_15 + ' (' + IntToStr(Head.Sectors) + ')' + ANSI_7 + endl);
- SetCurrentDir(FProgramDir);
- FileOpen := FALSE;
-
- try
- AssignFile(F, Filename);
- ReWrite(F, 1);
- FileOpen := TRUE;
- BlockWrite(F, Head, SizeOf(TDataHeader));
- NULLSector(Sect);
- for I := 0 to Head.Sectors do
- BlockWrite(F, Sect, SizeOf(Sect));
- finally
- if (FileOpen) then
- CloseFile(F);
- end;
- end;
- // ---------------------------
- // main storage/retrieval methods
- procedure TModDatabase.SaveSector(S: TSector; Index: Integer; ShipList: TList = nil; TraderList: TList = nil; PlanetList: TList = nil);
- var
- Sect : TSector;
- I,
- X : Integer;
- Bad,
- Found : Boolean;
- WarpsIn : TList;
- Product : TProductType;
- begin
- // Save this sector to file if set to record data
- if (Recording) and (DatabaseOpen) then
- begin
- Bad := (Index < 1) or (Index > DBHeader.Sectors);
- I := 1;
- while not Bad and (I <= 6) do
- begin
- if (S.Warp[I] > DBHeader.Sectors) then
- Bad := True;
-
- Inc(I);
- end;
- if Bad then
- begin
- SetForegroundWindow(Application.Handle);
- TWXServer.ClientMessage('Unable to store sector ''' + IntToStr(Index) + ''', closing database.');
- CloseDatabase;
- Exit;
- end;
- ReadData(@Sect, Index * SizeOf(TSector) + SizeOf(TDataHeader), SizeOf(Sect));
- // If this sector has been probed, recall warps if seen before
- if (S.Warp[1] = 0) and (Sect.Warp[1] <> 0) then
- S.Warp := Sect.Warp;
- // Don't go over density or anomoly readings unless this is a density scan
- if (S.Density = -1) then
- begin
- S.Density := Sect.Density;
- S.Anomoly := Sect.Anomoly;
- end;
- // Don't go over port details unless they are specified
- if (S.SPort.UpDate = 0) then
- begin
- S.SPort.Update := Sect.SPort.UpDate;
- for Product := Low(TProductType) to High(TProductType) do
- begin
- S.SPort.ProductAmount[Product] := Sect.SPort.ProductAmount[Product];
- S.SPort.ProductPercent[Product] := Sect.SPort.ProductPercent[Product];
- end;
- end;
- // save stardock details if this sector has it
- if (S.SPort.ClassIndex = 9) and (Sect.SPort.ClassIndex <> 9) then
- begin
- FDBHeader.StarDock := Index;
- WriteHeader;
- end;
- // update sector warp count
- S.Warps := 6;
- for I := 1 to 6 do
- if (S.Warp[I] = 0) then
- begin
- S.Warps := I - 1;
- Break;
- end;
- // Purge old ship, trader and planet data
- PurgeRecordList(Sect.Ships);
- PurgeRecordList(Sect.Traders);
- PurgeRecordList(Sect.Planets);
- // Write the ships to file
- S.Ships := WriteRecordList(ShipList, SizeOf(TShip));
- // Write the traders to file
- S.Traders := WriteRecordList(TraderList, SizeOf(TTrader));
- // Write the planets to file
- S.Planets := WriteRecordList(PlanetList, SizeOf(TPlanet));
- // write sector to database
- WriteData(@S, Index * SizeOf(TSector) + SizeOf(TDataHeader), SizeOf(TSector));
- // Update sector warp cache with new specs (if need be)
- for I := 1 to 6 do
- if (S.Warp[I] > 0) then
- begin
- WarpsIn := GetWarpsIn(S.Warp[I]);
- // see if its in there already
- X := 0;
- Found := FALSE;
- while (X < WarpsIn.Count) do
- begin
- if (TWarpIn(WarpsIn.Items[X]^).Origin = Index) then
- begin
- Found := TRUE;
- Break;
- end;
- Inc(X);
- end;
- WarpsIn.Free;
- if not (Found) then
- AddWarpIn(S.Warp[I], Index);
- end
- else
- Break;
- end;
- end;
- function TModDatabase.LoadSector(I : Integer) : TSector;
- begin
- if (I <= 0) or (I > DBHeader.Sectors) then
- raise EDatabaseError.Create('Unable to load sector: ' + IntToStr(I));
- if not (DatabaseOpen) then
- // no database or database is corrupt - load a blank sector
- NULLSector(Result)
- else
- ReadData(@Result, I * SizeOf(TSector) + SizeOf(TDataHeader), SizeOf(TSector));
- end;
- procedure TModDatabase.UpdateWarps(SectIndex : Integer);
- var
- I : Integer;
- S : TSector;
- begin
- // find out how many warps there are going out of this sector
- S := LoadSector(SectIndex);
- I := 1;
- while (I <= 6) do
- begin
- if (S.Warp[I] = 0) then
- Break;
- Inc(I);
- end;
- S.Warps := I;
- SaveSector(S, SectIndex, nil, nil, nil);
- end;
- function TModDatabase.GetSectorItems(ItemType : TSectorItem; Sector : TSector) : TList;
- begin
- Result := TList.Create;
- if not (DatabaseOpen) then
- // no database or database is corrupt
- Exit;
- if (ItemType = itPlanet) then
- ReadRecordList(Result, Sector.Planets)
- else if (ItemType = itTrader) then
- ReadRecordList(Result, Sector.Traders)
- else if (ItemType = itShip) then
- ReadRecordList(Result, Sector.Ships);
- end;
- function TModDatabase.GetWarpsIn(Sect : Integer) : TList;
- var
- W : PWarpIn;
- begin
- Result := TList.Create;
- if not (DatabaseOpen) then
- Exit;
- W := PWarpIn(Pointer(Integer(SectorWarpCache) + (Sect - 1) * 4)^);
- while (W <> nil) do
- begin
- Result.Add(W);
- W := W^.NextWarpIn;
- end;
- end;
- function TModDatabase.GetBackDoors(S: TSector; SectorIndex: Integer) : TList;
- var
- I : Integer;
- W : PWarpIn;
- Sect : ^Word;
- WarpsIn : TList;
- begin
- WarpsIn := GetWarpsIn(SectorIndex);
- Result := TList.Create;
- if not (DatabaseOpen) then
- // no database or database is corrupt
- Exit;
-
- I := 0;
- while (I < WarpsIn.Count) do
- begin
- W := PWarpIn(WarpsIn.Items[I]);
- if not (WarpsTo(S, W^.Origin)) then
- begin
- Sect := AllocMem(SizeOf(Word));
- Sect^ := W^.Origin;
- Result.Add(Sect);
- end;
- Inc(I);
- end;
- WarpsIn.Free;
- end;
- function TModDatabase.PlotWarpCourse(FromSect, ToSect : Word) : TList;
- var
- S : TSector;
- Warp : TWarpItem;
- Map,
- ItemDone : Pointer;
- I,
- X,
- ListSize,
- Avoid : Integer;
- CurWarp : TWarpItem;
- W : ^Word;
- procedure AddItem(Index : Word; Parent : TWarpItem);
- var
- NewWarp : TWarpItem;
- begin
- NewWarp := TWarpItem.Create;
- NewWarp.Parent := Parent;
- NewWarp.Index := Index;
- TWarpItem(Pointer(Integer(Map) + ListSize * SizeOf(Pointer))^) := NewWarp;
- // record this sector as added
- Byte(Pointer(Integer(ItemDone) + Index - 1)^) := 1;
- Inc(ListSize);
- end;
- begin
- Result := TList.Create;
- if not (DatabaseOpen) then
- // no database or database is corrupt
- Exit;
- Map := AllocMem((DBHeader.Sectors + 1) * SizeOf(Pointer));
- ItemDone := AllocMem(DBHeader.Sectors + 1);
- ListSize := 1;
- I := 0;
- Warp := TWarpItem.Create;
- Warp.Parent := nil;
- Warp.Index := FromSect;
- TWarpItem(Map^) := Warp;
- repeat
- if (I >= ListSize) then
- Break;
- CurWarp := TWarpItem(Pointer(Integer(Map) + I * SizeOf(Pointer))^);
- if (CurWarp.Index = ToSect) then
- begin
- // get the path
- Warp := CurWarp;
- while (Warp <> nil) do
- begin
- W := AllocMem(SizeOf(Word));
- W^ := Warp.Index;
- Result.Add(W);
- Warp := Warp.Parent;
- end;
- Break;
- end;
- // get the warps out and add them to the map
- S := LoadSector(CurWarp.Index);
- if (CurWarp.Parent = nil) then
- Avoid := 0
- else
- Avoid := CurWarp.Parent.Index;
- X := 1;
- while (X <= 6) and (S.Warp[X] <> 0) do
- begin
- // Add warp to the list
- if (S.Warp[X] <> Avoid) and (Byte(Pointer(Integer(ItemDone) + S.Warp[X] - 1)^) = 0) then
- AddItem(S.Warp[X], CurWarp);
- Inc(X);
- end;
- Inc(I);
- until (I >= DBHeader.Sectors);
- for I := ListSize - 1 downto 0 do
- TWarpItem(Pointer(Integer(Map) + I * SizeOf(Pointer))^).Free;
- FreeMem(Map);
- FreeMem(ItemDone);
- end;
- procedure TModDatabase.WriteHeader;
- begin
- if not (DatabaseOpen) then
- // no database or database is corrupt
- Exit;
- // update the DB header in file and cache
- WriteData(@DBHeader, 0, SizeOf(TDataHeader));
- end;
- procedure TModDatabase.DumpData;
- var
- Next,
- Pos : Integer;
- Size : Word;
- InUse : Byte;
- InUseStr : string;
- begin
- // dump stored records
- Pos := SizeOf(TSector) * (DBHeader.Sectors + 1) + SizeOf(TDataHeader);
- while (Pos < DBSize) do
- begin
- ReadData(@Size, Pos, 2);
- ReadData(@InUse, Pos + 2, 1);
- ReadData(@Next, Pos + 3, 4);
- if (InUse <> 0) then
- InUseStr := 'YES'
- else
- InUseStr := 'NO';
- TWXServer.Broadcast(endl + ANSI_15 + 'In use: ' + ANSI_14 + InUseStr);
- TWXServer.Broadcast(endl + ANSI_15 + 'Size: ' + ANSI_14 + IntToStr(Size));
- TWXServer.Broadcast(endl + ANSI_15 + 'Next: ' + ANSI_14 + IntToStr(Next) + endl);
- Inc(Pos, Size + 7);
- end;
- end;
- procedure TModDatabase.SetSectorVar(SectorIndex: Integer; const VarName, VarValue: string);
- var
- SectorVar: PSectorVar;
- VarOffset: Integer;
- P: Pointer;
- begin
- if (VarValue = '') then
- DeleteSectorVar(SectorIndex, VarName)
- else
- begin
- FindSectorVar(SectorIndex, VarName, SectorVar, VarOffset);
- if Assigned(SectorVar) then
- begin
- // Record already exists - write over the Value part of it
- SectorVar.Value := VarValue;
- // 7 Bytes record overhead + 10 bytes var name in record
- P := Pointer(Integer(SectorVar) + 10);
- WriteData(P, VarOffset + 17, 40);
- end
- else
- begin
- // Record doesn't exist - create a new one
- SectorVar := AllocMem(SizeOf(TSectorVar));
- SectorVar.VarName := VarName;
- SectorVar.Value := VarValue;
- AddSectorVar(SectorIndex, SectorVar);
- Dispose(SectorVar);
- end;
- end;
- end;
- function TModDatabase.GetSectorVar(SectorIndex: Integer; const VarName: string): string;
- var
- SectorVar: PSectorVar;
- VarOffset: Integer;
- begin
- // return value of this sector variable
- FindSectorVar(SectorIndex, VarName, SectorVar, VarOffset);
- if Assigned(SectorVar) then
- Result := SectorVar.Value
- else
- Result := '';
- end;
- // ---------------------------
- // null item retrieval
- procedure TModDatabase.NULLSector(var Sector : TSector);
- begin
- ZeroMemory(@Sector, SizeOf(Sector));
- Sector.Density := -1;
- end;
- procedure TModDatabase.NULLPlanet(var Planet : TPlanet);
- begin
- ZeroMemory(@Planet, SizeOf(Planet));
- end;
- procedure TModDatabase.NULLTrader(var Trader : TTrader);
- begin
- ZeroMemory(@Trader, SizeOf(Trader));
- end;
- procedure TModDatabase.NULLShip(var Ship : TShip);
- begin
- ZeroMemory(@Ship, SizeOf(Ship));
- end;
- // ***********************************************************
- // Protected Implementation
- procedure TModDatabase.AddSectorVar(SectorIndex: Integer; SectorVar: PSectorVar);
- const
- RecordSize = SizeOf(TSectorVar);
- var
- RecPtr,
- RecPos,
- NextRecord: Integer;
- S: TSector;
- begin
- // seek the end of the sector variable list for this sector and add the variable
- NextRecord := Sectors[SectorIndex].Vars;
- RecPtr := NextRecord;
- while (NextRecord <> 0) do
- begin
- RecPtr := NextRecord + 3;
- ReadData(@NextRecord, RecPtr, 4);
- end;
- RecPos := GetEmptyRecord(RecordSize);
- if (RecPtr = 0) then
- begin
- // update sector directly (no variables yet)
- S := LoadSector(SectorIndex);
- S.Vars := RecPos;
- SaveSector(S, SectorIndex);
- end
- else
- WriteData(@RecPos, RecPtr, 4);
- WriteRecord(SectorVar, RecPos, 0, RecordSize);
- end;
- procedure TModDatabase.FindSectorVar(SectorIndex: Integer; const VarName: string; var SectorVar: PSectorVar; var Index: Integer);
- const
- RecordSize = SizeOf(TSectorVar);
- var
- NextRecord : Integer;
- begin
- // Locate a sector variable of the specific name within the database, returning
- // a pointer to a record representing it in memory, and the index of its position
- // within the database.
- NextRecord := Sectors[SectorIndex].Vars;
- Index := 0;
- while (NextRecord <> 0) do
- begin
- SectorVar := AllocMem(RecordSize);
- ReadData(SectorVar, NextRecord + 7, RecordSize);
- if (SectorVar.VarName = VarName) then
- begin
- Index := NextRecord;
- Dispose(SectorVar);
- Break;
- end;
- Dispose(SectorVar);
- ReadData(@NextRecord, NextRecord + 3, 4);
- end;
- if (Index = 0) then
- SectorVar := nil;
- end;
- procedure TModDatabase.DeleteSectorVar(SectorIndex: Integer; const VarName: string);
- const
- RecordSize = SizeOf(TSectorVar);
- var
- LastRecord,
- ThisRecord,
- NextRecord: Integer;
- S: TSector;
- SectorVar: PSectorVar;
- begin
- // Find the sector var with a matching name and unlink it from the list,
- // flagging it as inactive.
- ThisRecord := Sectors[SectorIndex].Vars;
- LastRecord := 0;
- NextRecord := 0;
- while (ThisRecord <> 0) do
- begin
- SectorVar := AllocMem(RecordSize);
- ReadData(SectorVar, ThisRecord + 7, RecordSize);
- ReadData(@NextRecord, ThisRecord + 3, 4);
- if (SectorVar.VarName = VarName) then
- begin
- // update previous record
- if (LastRecord = 0) then
- begin
- // previous record is root (sector), update it
- S := LoadSector(SectorIndex);
- S.Vars := NextRecord;
- SaveSector(S, SectorIndex);
- end
- else
- begin
- // previous record is another record
- WriteData(@NextRecord, LastRecord + 3, 4);
- end;
- NextRecord := 0; // exit loop
- end;
- Dispose(SectorVar);
- LastRecord := ThisRecord;
- ThisRecord := NextRecord;
- end;
- end;
- procedure TModDatabase.AddWarpIn(Sect, Origin : Word);
- var
- P : Pointer;
- W : PWarpIn;
- begin
- W := AllocMem(SizeOf(TWarpIn));
- W^.Origin := Origin;
- // hook the new warp into this sector's warpin list
- P := Pointer(Integer(SectorWarpCache) + (Sect - 1) * SizeOf(Pointer));
- W^.NextWarpIn := PWarpIn(P^);
- Pointer(P^) := W;
- end;
- function TModDatabase.GetEmptyRecord(Size : Integer) : Integer;
- var
- I : Integer;
- begin
- Result := DBSize;
- I := 0;
- while (I < Length(EmptyRecordGroups)) do
- begin
- if (EmptyRecordGroups[I].RecordSize = Size) then
- begin
- if (Length(EmptyRecordGroups[I].EmptyRecords) > 0) then
- begin
- Result := EmptyRecordGroups[I].EmptyRecords[Length(EmptyRecordGroups[I].EmptyRecords) - 1];
- SetLength(EmptyRecordGroups[I].EmptyRecords, Length(EmptyRecordGroups[I].EmptyRecords) - 1);
- end;
- Break;
- end;
- Inc(I);
- end;
- end;
- procedure TModDatabase.CacheEmptyRecords;
- var
- Pos : Integer;
- Size : Word;
- InUse : Byte;
- begin
- // go through and find all empty records, storing them.
- Pos := SizeOf(TSector) * (DBHeader.Sectors + 1) + SizeOf(TDataHeader);
- while (Pos < DBSize) do
- begin
- ReadData(@Size, Pos, 2);
- ReadData(@InUse, Pos + 2, 1);
- if (InUse = 0) then
- CacheEmptyRecord(Pos, Size);
- Inc(Pos, Size + 7);
- end;
- end;
- procedure TModDatabase.CacheEmptyRecord(Index : Integer; Size : Word);
- var
- I : Integer;
- Found : Boolean;
- begin
- // loop through record cache lists, add record to a list matching its size
- Found := FALSE;
- I := 0;
- while (I < Length(EmptyRecordGroups)) do
- begin
- if (EmptyRecordGroups[I].RecordSize = Size) then
- begin
- Found := TRUE;
- Break;
- end;
- Inc(I);
- end;
- if not (Found) then
- begin
- I := Length(EmptyRecordGroups);
- SetLength(EmptyRecordGroups, I + 1);
- EmptyRecordGroups[I].RecordSize := Size;
- end;
- SetLength(EmptyRecordGroups[I].EmptyRecords, Length(EmptyRecordGroups[I].EmptyRecords) + 1);
- EmptyRecordGroups[I].EmptyRecords[Length(EmptyRecordGroups[I].EmptyRecords) - 1] := Index;
- end;
- procedure TModDatabase.ReadRecordList(List : TList; FirstPos : Integer);
- var
- NextRecord : Integer;
- RecordSize : Word;
- Rec : Pointer;
- begin
- NextRecord := FirstPos;
- while (NextRecord <> 0) do
- begin
- ReadData(@RecordSize, NextRecord, 2);
- Rec := AllocMem(RecordSize);
- ReadData(Rec, NextRecord + 7, RecordSize);
- List.Add(Rec);
- ReadData(@NextRecord, NextRecord + 3, 4);
- end;
- end;
- function TModDatabase.WriteRecordList(List : TList; RecordSize : Word) : Integer;
- var
- I,
- Pos,
- LastPos : Integer;
- begin
- Result := 0;
- if (List <> nil) then
- if (List.Count > 0) then
- begin
- LastPos := 0;
- for I := List.Count - 1 downto 0 do
- begin
- // Find the nearest zeroed record (or EOF)
- Pos := GetEmptyRecord(RecordSize);
- // Write it in
- WriteRecord(List[I], Pos, LastPos, RecordSize);
- LastPos := Pos;
- end;
- Result := LastPos;
- end;
- end;
- procedure TModDatabase.PurgeRecordList(FirstPos : Integer);
- var
- NextRecord : Integer;
- InUse : Byte;
- Size : Word;
- begin
- NextRecord := FirstPos;
- InUse := 0;
- while (NextRecord <> 0) do
- begin
- ReadData(@Size, NextRecord, 2);
- CacheEmptyRecord(NextRecord, Size);
- WriteData(@InUse, NextRecord + 2, 1);
- ReadData(@NextRecord, NextRecord + 3, 4);
- end;
- end;
- procedure TModDatabase.WriteRecord(Rec : Pointer; Pos, Next : Integer; Size : Word);
- var
- InUse : Byte;
- begin
- WriteData(@Size, Pos, 2);
- InUse := 1;
- WriteData(@InUse, Pos + 2, 1);
- WriteData(@Next, Pos + 3, 4);
- WriteData(Rec, Pos + 7, Size);
- end;
- procedure TModDatabase.ReadData(Data : Pointer; Index, Size : Integer);
- begin
- if (UseCache) then
- // cache enabled - read data directly from data cache
- CopyMemory(Data, Pointer(Integer(DataCache) + Index), Size)
- else
- begin
- // cache disabled - read data from file
- Seek(DataFile, Index);
- BlockRead(DataFile, Data^, Size);
- end;
- end;
- procedure TModDatabase.WriteData(Data : Pointer; Index, Size : Integer);
- var
- DataIndex : Pointer;
- CurDatabase : string;
- begin
- if (UseCache) then
- begin
- if (Index + Size > CacheSize) then
- begin
- // increase cache size
- try
- Inc(CacheSize, 50000);
- ReallocMem(DataCache, CacheSize);
- except
- UseCache := FALSE;
- CurDatabase := FDataFilename;
- TWXServer.ClientMessage('Not enough free memory available for cache extensions - database cache disabled');
- CloseDataBase;
- OpenDatabase(CurDatabase);
- WriteData(Data, Index, Size);
- Exit;
- end;
- end;
- DataIndex := Pointer(Integer(DataCache) + Index);
- // compare memory with cached data
- if not (CompareMem(DataIndex, Data, Size)) then
- begin
- // data is different - update data cache and file
- CopyMemory(DataIndex, Data, Size);
- Seek(DataFile, Index);
- BlockWrite(DataFile, Data^, Size);
- DBSize := FileSize(DataFile);
- end;
- end
- else
- begin
- // write data directly to file
- Seek(DataFile, Index);
- BlockWrite(DataFile, Data^, Size);
- DBSize := FileSize(DataFile);
- end;
- end;
- procedure TModDatabase.OpenCache;
- begin
- // load the database into cache
- if (CacheAllocated) then
- CloseCache;
- try
- CacheSize := FileSize(DataFile) + 50000;
- DataCache := AllocMem(CacheSize);
- CacheAllocated := TRUE;
- Seek(DataFile, 0);
- BlockRead(DataFile, DataCache^, FileSize(DataFile));
- except
- TWXServer.Broadcast(endl + ANSI_12 + 'Error while caching database, database cache has been disabled' + ANSI_7 + endl);
- UseCache := False;
- end;
- end;
- procedure TModDatabase.CloseCache;
- begin
- // release data cache
- if not (CacheAllocated) then
- Exit;
- FreeMem(DataCache, CacheSize);
- CacheSize := 0;
- CacheAllocated := FALSE;
- end;
- function TModDatabase.GetDatabaseName: string;
- begin
- Result := FDataFilename;
- end;
- procedure TModDatabase.SetDatabaseName(const Value: string);
- begin
- if (Value <> FDataFilename) then
- begin
- CloseDatabase;
- OpenDatabase(Value);
- end;
- end;
- function TModDatabase.GetUseCache: Boolean;
- begin
- Result := FUseCache;
- end;
- procedure TModDatabase.SetUseCache(Value: Boolean);
- begin
- if (FUseCache <> Value) then
- begin
- FUseCache := Value;
- if (FUseCache) then
- OpenCache
- else
- CloseCache;
- end;
- end;
- function TModDatabase.GetRecording: Boolean;
- begin
- Result := FRecording;
- end;
- procedure TModDatabase.SetRecording(Value: Boolean);
- begin
- FRecording := Value;
- TWXGUI.Recording := Value;
- end;
- function TModDatabase.GetLastPortCIM: TDateTime;
- begin
- Result := DBHeader.LastPortCIM;
- end;
- procedure TModDatabase.SetLastPortCIM(const Value: TDateTime);
- begin
- FDBHeader.LastPortCIM := Value;
- end;
- end.
|