aboutsummaryrefslogtreecommitdiffstats
path: root/Game/Code/Classes/UCovers.pas
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--Game/Code/Classes/UCovers.pas539
1 files changed, 390 insertions, 149 deletions
diff --git a/Game/Code/Classes/UCovers.pas b/Game/Code/Classes/UCovers.pas
index 1d3f737c..d3665854 100644
--- a/Game/Code/Classes/UCovers.pas
+++ b/Game/Code/Classes/UCovers.pas
@@ -1,5 +1,23 @@
unit UCovers;
+{********************
+ UCover
+ Contains Class managing Covers.Cache File
+ File Structure:
+ TCC_FileHeader
+
+ TextureData
+ * Array of TCC_TextureData
+
+ Indexes
+ * TCC_FileIndex Header Block
+ * TCC_FileIndex
+ * String containing Filename of Last IndexEntry. Ending with #0
+ .
+ .
+ * TCC_FileIndex Footer Block
+*********************}
+
interface
{$IFDEF FPC}
@@ -21,23 +39,81 @@ uses OpenGL12,
UThemes,
UTexture;
+const
+ cCC_CoverW = 128;
+ cCC_CoverH = 128;
+ cCC_CoverSize = cCC_CoverW * cCC_CoverH * 3;
+ cCC_HeaderText = 'USDxCo' + #0 + #1;
+ cCC_HeaderVersion = 1000;
+ cCC_IndexIndicator= 'I' + 'N' + 'D' + #0;
+
type
TCover = record
Name: string;
W: word;
H: word;
Size: integer;
- Position: integer; // position of picture in the cache file
+ Position: integer; //position of picture in the cache file
// Data: array of byte;
end;
+ //-------------------------------------------------------------------
+ //Covers.Cache File Parts
+
+ TCC_Hash = Array [1..32] of Char;
+ TCC_FileHeader = record
+ FileTyp: Array [1..8] of Char; //Some String to detect if this is the file we want to open
+ Version: DWord; //Version of Covers.Cache File
+ Hash: TCC_Hash; //Some Randomly Created Alphanumeric String to Identify w/ SongDB
+ CoverW: Word; //Width of all Covers in Cache
+ CoverH: Word; //Height of all Covers in Cache
+ DataStart: Cardinal; //Start Position in Bytes of Data Part
+ DataLength: Cardinal; //Start Position in Bytes of Data Part
+ IndexStart: Cardinal; //Start of Index Block in Bytes
+ end;
+
+ PCC_TextureData = ^TCC_TextureData;
+ TCC_TextureData = Array [0..cCC_CoverSize - 1] of Byte;
+
+ TCC_FileIndexHeader = record
+ Indicator: Array [1..4] of Char; //Contains INDE
+ HighestID: Cardinal; //Highest ID of a Cover
+ end;
+
+ TCC_FileIndex = record
+ LastUpdated: Integer; //Time of LastFilechange
+ DataStart: Cardinal; //Position of TextureData of this Cover in Bytes.
+ //0 if this is empty slot(Deleted Cover)(Id not available)
+ //1 if the Texture Data is not already loaded to Cache
+ //High(Cardinal) if this is IndexFooter
+ //Filename: String;
+ end;
+
+ TCC_IndexListItem = record
+ Filename: String;
+ TexID: Integer;
+ FileIndex: TCC_FileIndex;
+ end;
+ TCC_IndexList = Array of TCC_IndexListItem;
+
TCovers = class
private
- Filename: String;
+ Filename: String;
+
+ Header: TCC_FileHeader;
+ HighestID: Integer;
+ Count: Cardinal;
+ Index: TCC_IndexList;
+ IndexNeedRewrite: Boolean; //Index in CacheFile is overwritten by other Data
+ CacheReadOnly: Boolean; //Cache File is read only
- WritetoFile: Boolean;
+ Function WriteHeader:Boolean;
+ Function ReadHeader: Boolean;
+ Function ReadIndex: Boolean;
+ Function WriteIndex: Boolean;
+ Function AddTexData(Data: PCC_TextureData): Cardinal;
public
W: word;
H: word;
@@ -45,14 +121,15 @@ type
Data: array of byte;
Cover: array of TCover;
- constructor Create;
- procedure Load;
- procedure Save;
+ property Hash: TCC_Hash read Header.Hash;
+
+ constructor Create(const Filename: String);
+ procedure Load(const Filename: String);
Function AddCover(FileName: string): Integer; //Returns ID, Checks Cover for Change, Updates Cover if required
- function CoverExists(FileName: string): boolean;
- function CoverNumber(FileName: string): integer; //Returns ID by FilePath
+ function CoverExists(FileName: string): Integer; //Returns ID by FilePath
procedure PrepareData(FileName: string);
Procedure LoadTextures;
+ Function ReWriteCache: Boolean; //Deletes old cover.cache file and writes new one
end;
var
@@ -65,189 +142,353 @@ uses UMain,
ULog,
DateUtils;
-constructor TCovers.Create;
+constructor TCovers.Create(const Filename: String);
begin
- W := 128;
- H := 128;
- Size := W*H*3;
- Load;
- WritetoFile := True;
+ HighestID := -1;
+ SetLength(Index, HighestID + 2);
+ Load(Filename);
end;
-procedure TCovers.Load;
+//----------------------------------------------
+// Some File handling helpers
+
+//--------
+// Reads and Checks Header. Returns True if Header is correct
+//--------
+Function TCovers.ReadHeader: Boolean;
var
- F: File;
- C: integer; // cover number
- W: word;
- H: word;
- Bits: byte;
- NLen: word;
- Name: string;
-// Data: array of byte;
+ F: File of TCC_FileHeader;
begin
- if FileExists(GamePath + 'covers.cache') then
- begin
- AssignFile(F, GamePath + 'covers.cache');
- Reset(F, 1);
-
- WritetoFile := not FileIsReadOnly(GamePath + 'covers.cache');
-
- SetLength(Cover, 0);
+ try
+ //Read Header
+ AssignFile(F, Filename);
+
+ try
+ Reset(F);
+ Read(F, Header);
+ finally
+ CloseFile(F);
+ end;
- while not EOF(F) do
+ //Check Header
+ If (Header.FileTyp = cCC_HeaderText) AND
+ (Header.Version = cCC_HeaderVersion) then
begin
- SetLength(Cover, Length(Cover)+1);
-
- BlockRead(F, W, 2);
- Cover[High(Cover)].W := W;
-
- BlockRead(F, H, 2);
- Cover[High(Cover)].H := H;
+ Result := True;
+ IndexNeedRewrite := True;
+ end
+ Else
+ Result := False;
- BlockRead(F, Bits, 1);
- Cover[High(Cover)].Size := W * H * (Bits div 8);
-
- // test
- // W := 128;
- // H := 128;
- // Bits := 24;
- // Seek(F, FilePos(F) + 3);
+ except
+ Result := False;
+ end;
+end;
- BlockRead(F, NLen, 2);
- SetLength(Name, NLen);
+//--------
+// Writes Header(Resets File). Returns True if Writing succeed
+//--------
+Function TCovers.WriteHeader:Boolean;
+var
+ F: File of TCC_FileHeader;
+begin
+ try
+ Result := True;
+ //Read Header
+ AssignFile(F, Filename);
+ try
+ If (not FileExists(Filename)) then
+ ReWrite(F)
+ else
+ Reset(F);
+
+ Write(F, Header);
+ finally
+ CloseFile(F);
+ end;
+ except
+ Result := False;
+ end;
+end;
- BlockRead(F, Name[1], NLen);
- Cover[High(Cover)].Name := Name;
+//--------
+// Reads and Checks Index. Returns True if Index is correct
+//--------
+Function TCovers.ReadIndex: Boolean;
+var
+ F: File of Byte;
+ IndexHeader: TCC_FileIndexHeader;
+ I: Integer;
- Cover[High(Cover)].Position := FilePos(F);
- Seek(F, FilePos(F) + W*H*(Bits div 8));
+ Procedure mReadLn(var S: String);
+ var J: Integer;
+ begin
+ S := '';
+ J := 0;
+
+ Repeat
+ Inc(J);
+ SetLength(S, J);
+ Read(F, Byte(S[J]));
+ Until S[J] = #10
+ end;
+begin
+ try
+ //Read Header
+ AssignFile(F, Filename);
+ try
+ Reset(F);
+ Seek(F, Header.IndexStart);
+
+ BlockRead(F, IndexHeader, SizeOf(TCC_FileIndexHeader));
+
+ If (IndexHeader.Indicator = cCC_IndexIndicator) then
+ begin
+ Result := False;
+
+ HighestID := IndexHeader.HighestID;
+ SetLength(Index, HighestID + 2);
+
+ Count := 0;
+ Result := True;
+ If (HighestID > 0) then
+ begin
+ //Read File Infos until (Eof or Footer)
+ I := 0;
+ While (Not Eof(F)) AND ((I <= 0) OR (Index[I].FileIndex.DataStart <> High(Cardinal))) do
+ begin
+ If (I > HighestID) then
+ begin //Header IndexCOunt was wrong, running out of array
+ Inc(HighestID);
+ IndexNeedReWrite := True;
+ SetLength(Index, HighestID + 2);
+ end;
+
+ BlockRead(F, Index[I].FileIndex, SizeOf(TCC_FileIndex));
+ Index[I].TexID := -1;
+
+ If (Index[I].FileIndex.DataStart <> High(Cardinal)) AND (Not Eof(F)) then
+ begin
+ //Read Filename
+ mReadLn(Index[I].Filename);
+ Inc(Count);
+ end;
+
+ Inc(I);
+ end;
+
+ If (Index[HighestID + 1].FileIndex.DataStart = High(Cardinal)) then
+ begin //No Footer found
+ IndexNeedReWrite := True;
+ end;
+ end;
+
+ end;
+
+ finally
+ CloseFile(F);
+ end;
+ except
+ Result := False;
+ end;
+end;
- // SetLength(Cover[High(Cover)].Data, W*H*(Bits div 8));
- // BlockRead(F, Cover[High(Cover)].Data[0], W*H*(Bits div 8));
+//--------
+// Writes Index. Returns True if Writing succeed
+//--------
+Function TCovers.WriteIndex: Boolean;
+var
+ F: File of Byte;
+ IndexHeader: TCC_FileIndexHeader;
+ I: Integer;
- end; // While
+ Procedure mWriteLn(var S: String);
+ var N: Byte;
+ begin
+ BlockWrite(F, S, Length(S));
+ N := Byte(#10);
+ Write(F, N);
+ end;
+begin
+ Result := WriteHeader;
- CloseFile(F);
- end; // fileexists
+ If (Result) then
+ begin
+ try
+ //Read Header
+ AssignFile(F, Filename);
+ try
+ Reset(F);
+
+ Seek(F, Header.IndexStart);
+ //Write Header
+ IndexHeader.Indicator := cCC_IndexIndicator;
+ IndexHeader.HighestID := HighestID;
+
+ BlockWrite(F, IndexHeader, SizeOf(TCC_FileIndexHeader));
+
+ Count := 0;
+ Result := True;
+
+ //Prepare Footer
+ Index[HighestID + 1].FileIndex.DataStart := High(Cardinal);
+
+ // Write Fileinfo
+ For I := 0 to HighestID+1 do
+ begin
+ BlockWrite(F, Index[I].FileIndex, SizeOf(TCC_FileIndex));
+ mWriteLn(Index[I].Filename);
+ end;
+
+ finally
+ CloseFile(F);
+ end;
+ except
+ Result := False;
+ end;
+ end;
end;
-procedure TCovers.Save;
+//--------
+// Writes some Texture Data to the End of TextureData Block
+//--------
+Function TCovers.AddTexData(Data: PCC_TextureData): Cardinal;
var
- F: File;
- C: integer; // cover number
- W: word;
- H: word;
- NLen: word;
- Bits: byte;
+ F: File of Byte;
begin
-{ AssignFile(F, GamePath + 'covers.cache');
- Rewrite(F, 1);
-
- Bits := 24;
- for C := 0 to High(Cover) do begin
- W := Cover[C].W;
- H := Cover[C].H;
-
- BlockWrite(F, W, 2);
- BlockWrite(F, H, 2);
- BlockWrite(F, Bits, 1);
-
- NLen := Length(Cover[C].Name);
- BlockWrite(F, NLen, 2);
- BlockWrite(F, Cover[C].Name[1], NLen);
- BlockWrite(F, Cover[C].Data[0], W*H*(Bits div 8));
- end;
+ try
+ AssignFile(F, Filename);
+ try
+ Reset(F);
+ Seek(F, Header.DataStart + Header.DataLength);
+
+ BlockWrite(F, Data, SizeOf(TCC_TextureData));
- CloseFile(F);}
+ Result := Header.DataStart + Header.DataLength;
+ Inc(Header.DataLength, SizeOf(TCC_TextureData));
+
+ Header.IndexStart := Header.DataStart + Header.DataLength + 1;
+ IndexNeedReWrite := True;
+ finally
+ CloseFile(F);
+ end;
+ except
+ Result := 0;
+ end;
end;
-Function TCovers.AddCover(FileName: string): Integer;
+procedure TCovers.Load(const Filename: String);
var
- B: integer;
- F: File;
- C: integer; // cover number
- NLen: word;
- Bits: byte;
+ Succeed: Boolean;
begin
- if not CoverExists(FileName) then
+ Self.Filename := Filename;
+ Succeed := False;
+ If (FileExists(Filename)) then
begin
- SetLength(Cover, Length(Cover)+1);
- Cover[High(Cover)].Name := FileName;
-
- Cover[High(Cover)].W := W;
- Cover[High(Cover)].H := H;
- Cover[High(Cover)].Size := Size;
-
- // do not copy data. write them directly to file
-// SetLength(Cover[High(Cover)].Data, Size);
-// for B := 0 to Size-1 do
-// Cover[High(Cover)].Data[B] := CacheMipmap[B];
-
- if WritetoFile then
- begin
- AssignFile(F, GamePath + 'covers.cache');
-
- if FileExists(GamePath + 'covers.cache') then
- begin
- Reset(F, 1);
- Seek(F, FileSize(F));
- end
- else
+ CacheReadOnly := FileisReadOnly(Filename);
+ If (ReadHeader) then
+ begin //Header successful read
+ If (ReadIndex) then
begin
- Rewrite(F, 1);
+ Succeed := True;
end;
+ end;
+ end;
- Bits := 24;
-
- BlockWrite(F, W, 2);
- BlockWrite(F, H, 2);
- BlockWrite(F, Bits, 1);
-
- NLen := Length(FileName);
- BlockWrite(F, NLen, 2);
- BlockWrite(F, FileName[1], NLen);
+ If not Succeed and not CacheReadOnly then
+ If not (ReWriteCache) then
+ CacheReadOnly := True;
+end;
- Cover[High(Cover)].Position := FilePos(F);
- BlockWrite(F, CacheMipmap[0], W*H*(Bits div 8));
+Function TCovers.AddCover(FileName: string): Integer;
+var I: Integer;
+begin
+ Result := CoverExists(Filename);
+ If (Result = -1) then
+ begin //Add Cover(Does not exist)
+ If (Count <= HighestID) then
+ begin //There is an empty slot, Search It
+ For I := 0 to HighestID do
+ If (Index[I].FileIndex.DataStart = 0) then
+ begin //Found the Slot
+ Result := I;
+ Break;
+ end;
+ end;
- CloseFile(F);
+ If (Result = -1) then
+ begin //Attach it to the End
+ Inc(HighestID);
+ SetLength(Index, HighestID + 2);
+ Result := HighestID;
end;
+
+ Index[Result].Filename := Filename;
+ Index[Result].TexID := -1;
+ Index[Result].FileIndex.DataStart := 1;
end
else
- Cover[High(Cover)].Position := 0;
+ begin //Check if File has Changed
+ If (Index[Result].FileIndex.LastUpdated < 0) then
+ begin
+
+ end;
+ end;
end;
-function TCovers.CoverExists(FileName: string): boolean;
+Function TCovers.CoverExists(FileName: string): integer;
var
- C: integer; // cover
+ I: integer;
begin
- Result := false;
- C := 0;
-
- while (C <= High(Cover)) and (Result = false) do
+ Result := -1;
+ {$IFDEF MSWINDOWS}
+ Filename := Uppercase(Filename);
+ {$ENDIF}
+
+ For I := 0 to HighestID do
begin
- if Cover[C].Name = FileName then
- Result := true;
-
- Inc(C);
+ If (Index[I].FileIndex.DataStart <> 0) AND
+ {$IFDEF MSWINDOWS}
+ (Uppercase(Index[I].Filename) = Filename) then
+ {$ELSE}
+ (Index[I].Filename = Filename)
+ {$ENDIF}
+ begin
+ Result := I;
+ Exit;
+ end;
end;
end;
-function TCovers.CoverNumber(FileName: string): integer;
-var
- C: integer;
-begin
- Result := -1;
- C := 0;
-
- while (C <= High(Cover)) and (Result = -1) do
+//--------
+// Deletes old cover.cache file and writes new one
+//--------
+Function TCovers.ReWriteCache: Boolean;
+
+ Function MakeHash: TCC_Hash;
+ const AlphaNumeric: Array[0..35] of Char = ('A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9');
+ var I: Integer;
begin
- if Cover[C].Name = FileName then
- Result := C;
-
- Inc(C);
+ For I := Low(Result) to High(Result) do
+ Result[I] := AlphaNumeric[Random(Length(AlphaNumeric))];
end;
+begin
+ If not CacheReadOnly then
+ begin
+ Header.FileTyp := cCC_HeaderText;
+ Header.Version := cCC_HeaderVersion;
+ Header.Hash := MakeHash;
+ Header.CoverW := cCC_CoverW;
+ Header.CoverH := cCC_CoverH;
+ Header.DataStart := SizeOf(TCC_FileHeader) + 4; //Total of 64 Bytes (4 Bytes Space)
+ Header.DataLength := 0;
+ Header.IndexStart := Header.DataStart + Header.DataLength + 4;
+
+ Result := WriteIndex;
+ end
+ else
+ Result := False;
end;
procedure TCovers.PrepareData(FileName: string);
@@ -260,7 +501,7 @@ begin
AssignFile(F, GamePath + 'covers.cache');
Reset(F, 1);
- C := CoverNumber(FileName);
+ C := CoverExists(FileName);
SetLength(Data, Cover[C].Size);
if Length(Data) < 6 then
Log.LogStatus('Length(Data) < 6', 'TCovers.PrepareData');