From e7f714ccc3908b6b8f3fb11f82481780e223ee43 Mon Sep 17 00:00:00 2001 From: whiteshark0 Date: Fri, 4 Apr 2008 21:00:50 +0000 Subject: Covers Cache rewritten New Cover.Cache fileformat covers texture loading still missing... git-svn-id: svn://svn.code.sf.net/p/ultrastardx/svn/trunk@1001 b956fd51-792f-4845-bead-9b4dfca2ff2c --- Game/Code/Classes/UCovers.pas | 539 ++++++++--- Game/Code/Classes/UMain.pas | 1876 ++++++++++++++++++------------------- Game/Code/Classes/USongs.pas | 2 +- Game/Code/Screens/UScreenSong.pas | 22 +- 4 files changed, 1337 insertions(+), 1102 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'); diff --git a/Game/Code/Classes/UMain.pas b/Game/Code/Classes/UMain.pas index baefeff8..898c7193 100644 --- a/Game/Code/Classes/UMain.pas +++ b/Game/Code/Classes/UMain.pas @@ -1,873 +1,868 @@ -unit UMain; - -interface - -{$IFDEF FPC} - {$MODE Delphi} -{$ENDIF} - -{$I switches.inc} - -uses - SDL, - UGraphic, - UMusic, - URecord, - UTime, - SysUtils, - UDisplay, - UIni, - ULog, - ULyrics, - UScreenSing, - USong, - OpenGL12, - UThemes; - -type - TPlayer = record - Name: string; - - // Index in Teaminfo record - TeamID: Byte; - PlayerID: Byte; - - // Scores - Score: real; - ScoreLine: real; - ScoreGolden: real; - - ScoreI: integer; - ScoreLineI: integer; - ScoreGoldenI: integer; - ScoreTotalI: integer; - - // LineBonus Mod - ScoreLast: Real;//Last Line Score - - // PerfectLineTwinkle Mod (effect) - LastSentencePerfect: Boolean; - - //Meter: real; - - HighNote: integer; - IlNut: integer; - Note: array of record - Start: integer; - Length: integer; - Detekt: real; // accurate place, detected in the note - Tone: real; - Perfect: boolean; // true if the note matches the original one, lit the star - - // Half size Notes Patch - Hit: boolean; // true if the note Hits the Line - end; - end; - - -var - // Absolute Paths - GamePath: string; - SoundPath: string; - SongPath: string; - LogPath: string; - ThemePath: string; - SkinsPath: string; - ScreenshotsPath: string; - CoversPath: string; - LanguagesPath: string; - PluginPath: string; - VisualsPath: string; - PlayListPath: string; - - UserSongPath: string = ''; - UserCoversPath: string = ''; - UserPlaylistPath: string = ''; - - OGL: Boolean; - Done: Boolean; - Event: TSDL_event; - FileName: string; - Restart: boolean; - - // player and music info - Player: array of TPlayer; - PlayersPlay: integer; - - CurrentSong : TSong; - -procedure InitializePaths; - -Procedure Main; -procedure MainLoop; -procedure CheckEvents; -procedure Sing(Sender: TScreenSing); -procedure NewSentence(Sender: TScreenSing); -procedure NewBeat(Sender: TScreenSing); // executed when on then new beat -procedure NewBeatC(Sender: TScreenSing); // executed when on then new beat for click -procedure NewBeatD(Sender: TScreenSing); // executed when on then new beat for detection -//procedure NewHalf; // executed when in the half between beats -procedure NewNote(Sender: TScreenSing); // detect note -function GetMidBeat(Time: real): real; -function GetTimeFromBeat(Beat: integer): real; -procedure ClearScores(PlayerNum: integer); - -implementation - -uses - USongs, - UJoystick, - math, - UCommandLine, - ULanguage, - SDL_ttf, - USkins, - UCovers, - UCatCovers, - UDataBase, - UPlaylist, - UDLLManager, - UParty, - UConfig, - UCore, - UGraphicClasses, - UPluginDefs, - UPlatform; - - - - -procedure Main; -var - WndTitle: string; -begin - try - WndTitle := USDXVersionStr; - - if Platform.TerminateIfAlreadyRunning( {var} WndTitle) then - Exit; - - //------------------------------ - //StartUp - Create Classes and Load Files - //------------------------------ - USTime := TTime.Create; - - // Commandline Parameter Parser - Params := TCMDParams.Create; - - // Log + Benchmark - Log := TLog.Create; - Log.Title := WndTitle; - Log.Enabled := not Params.NoLog; - Log.BenchmarkStart(0); - - // Language - Log.BenchmarkStart(1); - Log.LogStatus('Initialize Paths', 'Initialization'); - InitializePaths; - Log.LogStatus('Load Language', 'Initialization'); - Language := TLanguage.Create; - - // Add Const Values: - Language.AddConst('US_VERSION', USDXVersionStr); - Log.BenchmarkEnd(1); - Log.LogBenchmark('Loading Language', 1); - - // SDL - Log.BenchmarkStart(1); - Log.LogStatus('Initialize SDL', 'Initialization'); - SDL_Init(SDL_INIT_VIDEO); - Log.BenchmarkEnd(1); - Log.LogBenchmark('Initializing SDL', 1); - - // SDL_ttf - Log.BenchmarkStart(1); - Log.LogStatus('Initialize SDL_ttf', 'Initialization'); - TTF_Init(); - Log.BenchmarkEnd(1); - Log.LogBenchmark('Initializing SDL_ttf', 1); - - // Skin - Log.BenchmarkStart(1); - Log.LogStatus('Loading Skin List', 'Initialization'); - Skin := TSkin.Create; - Log.BenchmarkEnd(1); - Log.LogBenchmark('Loading Skin List', 1); - - // Ini + Paths - Log.BenchmarkStart(1); - Log.LogStatus('Load Ini', 'Initialization'); - Ini := TIni.Create; - Ini.Load; - - //Load Languagefile - if (Params.Language <> -1) then - Language.ChangeLanguage(ILanguage[Params.Language]) - else - Language.ChangeLanguage(ILanguage[Ini.Language]); - - Log.BenchmarkEnd(1); - Log.LogBenchmark('Loading Ini', 1); - - // Sound - Log.BenchmarkStart(1); - Log.LogStatus('Initialize Sound', 'Initialization'); - InitializeSound(); - Log.BenchmarkEnd(1); - Log.LogBenchmark('Initializing Sound', 1); - - // Load Sound Settings from Ini - Log.BenchmarkStart(1); - Log.LogStatus('Load Sound Settings', 'Initialization'); - Ini.LoadSoundSettings; - Log.BenchmarkEnd(1); - Log.LogBenchmark('Load Sound Settings', 1); - - // Theme - Log.BenchmarkStart(1); - Log.LogStatus('Load Themes', 'Initialization'); - Theme := TTheme.Create(ThemePath + ITheme[Ini.Theme] + '.ini', Ini.Color); - Log.BenchmarkEnd(1); - Log.LogBenchmark('Loading Themes', 1); - - // Covers Cache - Log.BenchmarkStart(1); - Log.LogStatus('Creating Covers Cache', 'Initialization'); - Covers := TCovers.Create; - Log.LogBenchmark('Loading Covers Cache Array', 1); - Log.BenchmarkStart(1); - - // Category Covers - Log.BenchmarkStart(1); - Log.LogStatus('Creating Category Covers Array', 'Initialization'); - CatCovers:= TCatCovers.Create; - Log.BenchmarkEnd(1); - Log.LogBenchmark('Loading Category Covers Array', 1); - - // Songs - //Log.BenchmarkStart(1); - Log.LogStatus('Creating Song Array', 'Initialization'); - Songs := TSongs.Create; - //Songs.LoadSongList; - - Log.LogStatus('Creating 2nd Song Array', 'Initialization'); - CatSongs := TCatSongs.Create; - - Log.BenchmarkEnd(1); - Log.LogBenchmark('Loading Songs', 1); - - // PluginManager - Log.BenchmarkStart(1); - Log.LogStatus('PluginManager', 'Initialization'); - DLLMan := TDLLMan.Create; // Load PluginList - Log.BenchmarkEnd(1); - Log.LogBenchmark('Loading PluginManager', 1); - - {// Party Mode Manager - Log.BenchmarkStart(1); - Log.LogStatus('PartySession Manager', 'Initialization'); - PartySession := TPartySession.Create; //Load PartySession - - Log.BenchmarkEnd(1); - Log.LogBenchmark('Loading PartySession Manager', 1); } - - // Graphics - Log.BenchmarkStart(1); - Log.LogStatus('Initialize 3D', 'Initialization'); - Initialize3D(WndTitle); - Log.BenchmarkEnd(1); - Log.LogBenchmark('Initializing 3D', 1); - - // Score Saving System - Log.BenchmarkStart(1); - Log.LogStatus('DataBase System', 'Initialization'); - DataBase := TDataBaseSystem.Create; - - if (Params.ScoreFile = '') then - DataBase.Init ('Ultrastar.db') - else - DataBase.Init (Params.ScoreFile); - - Log.BenchmarkEnd(1); - Log.LogBenchmark('Loading DataBase System', 1); - - // Playlist Manager - Log.BenchmarkStart(1); - Log.LogStatus('Playlist Manager', 'Initialization'); - PlaylistMan := TPlaylistManager.Create; - Log.BenchmarkEnd(1); - Log.LogBenchmark('Loading Playlist Manager', 1); - - // GoldenStarsTwinkleMod - Log.BenchmarkStart(1); - Log.LogStatus('Effect Manager', 'Initialization'); - GoldenRec := TEffectManager.Create; - Log.BenchmarkEnd(1); - Log.LogBenchmark('Loading Particel System', 1); - - // Joypad - if (Ini.Joypad = 1) OR (Params.Joypad) then - begin - Log.BenchmarkStart(1); - Log.LogStatus('Initialize Joystick', 'Initialization'); - Joy := TJoy.Create; - Log.BenchmarkEnd(1); - Log.LogBenchmark('Initializing Joystick', 1); - end; - - Log.BenchmarkEnd(0); - Log.LogBenchmark('Loading Time', 0); - - Log.LogError('Creating Core'); - Core := TCore.Create( - USDXShortVersionStr, - MakeVersion(USDX_VERSION_MAJOR, - USDX_VERSION_MINOR, - USDX_VERSION_RELEASE, - chr(0)) - ); - - Log.LogError('Running Core'); - Core.Run; - - //------------------------------ - //Start- Mainloop - //------------------------------ - Log.LogStatus('Main Loop', 'Initialization'); - MainLoop; - - finally - //------------------------------ - //Finish Application - //------------------------------ - - // TODO: - // call an uninitialize routine for every initialize step - // or at least use the corresponding Free-Methods - - UnloadOpenGL; - //TTF_quit(); - SDL_Quit(); - - (* - {$ifdef WIN32} - if assigned(LCD) and (Ini.LPT = 1) then - LCD.Clear; - if assigned(Light) and (Ini.LPT = 2) then - Light.TurnOff; - {$endif} - *) - - if assigned(Log) then - begin - Log.LogStatus('Main Loop', 'Finished'); - Log.Free; - end; - end; -end; - -procedure MainLoop; -var - Delay: integer; -begin - Delay := 0; - SDL_EnableKeyRepeat(125, 125); - - CountSkipTime(); // JB - for some reason this seems to be needed when we use the SDL Timer functions. - while not Done do - begin - // joypad - if (Ini.Joypad = 1) or (Params.Joypad) then - Joy.Update; - - // keyboard events - CheckEvents; - - // display - done := not Display.Draw; - SwapBuffers; - - // light - //Light.Refresh; - - // delay - CountMidTime; - - Delay := Floor(1000 / 100 - 1000 * TimeMid); - - if Delay >= 1 then - SDL_Delay(Delay); // dynamic, maximum is 100 fps - - CountSkipTime; - - // reinitialization of graphics - if Restart then - begin - Reinitialize3D; - Restart := false; - end; - - end; -End; - -procedure CheckEvents; -begin - if Assigned(Display.NextScreen) then - Exit; - - while SDL_PollEvent( @event ) = 1 do - begin - case Event.type_ of - SDL_QUITEV: - begin - Display.Fade := 0; - Display.NextScreenWithCheck := nil; - Display.CheckOK := True; - end; - { - SDL_MOUSEBUTTONDOWN: - with Event.button Do - begin - if State = SDL_BUTTON_LEFT Then - begin - // - end; - end; - } - SDL_VIDEORESIZE: - begin - ScreenW := Event.resize.w; - ScreenH := Event.resize.h; - - screen := SDL_SetVideoMode(ScreenW, ScreenH, (Ini.Depth+1) * 16, SDL_OPENGL or SDL_RESIZABLE); - end; - SDL_KEYDOWN: - begin - // remap the "keypad enter" key to the "standard enter" key - if (Event.key.keysym.sym = SDLK_KP_ENTER) then - Event.key.keysym.sym := SDLK_RETURN; - - if (Event.key.keysym.sym = SDLK_F11) or - ((Event.key.keysym.sym = SDLK_RETURN) and - ((Event.key.keysym.modifier and KMOD_ALT) <> 0)) then // toggle full screen - begin - Ini.FullScreen := integer( not boolean( Ini.FullScreen ) ); - - if boolean( Ini.FullScreen ) then - begin - SDL_SetVideoMode(ScreenW, ScreenH, (Ini.Depth+1) * 16, SDL_OPENGL or SDL_FULLSCREEN); - SDL_ShowCursor(0); - end - else - begin - SDL_SetVideoMode(ScreenW, ScreenH, (Ini.Depth+1) * 16, SDL_OPENGL or SDL_RESIZABLE); - SDL_ShowCursor(1); - end; - - glViewPort(0, 0, ScreenW, ScreenH); - end - // ScreenShot hack. If Print is pressed-> Make screenshot and Save to Screenshots Path - else if (Event.key.keysym.sym = SDLK_SYSREQ) or (Event.key.keysym.sym = SDLK_PRINT) then - Display.ScreenShot - // popup hack... if there is a visible popup then let it handle input instead of underlying screen - // shoud be done in a way to be sure the topmost popup has preference (maybe error, then check) - else if (ScreenPopupError <> nil) and (ScreenPopupError.Visible) then - done := not ScreenPopupError.ParseInput(Event.key.keysym.sym, WideChar(Event.key.keysym.unicode), True) - else if (ScreenPopupCheck <> nil) and (ScreenPopupCheck.Visible) then - done := not ScreenPopupCheck.ParseInput(Event.key.keysym.sym, WideChar(Event.key.keysym.unicode), True) - // end of popup hack - else - begin - // check for Screen want to Exit - done := not Display.CurrentScreen^.ParseInput(Event.key.keysym.sym, WideChar(Event.key.keysym.unicode), True); - - // If Screen wants to Exit - if done then - begin - // If Question Option is enabled then Show Exit Popup - if (Ini.AskbeforeDel = 1) then - begin - Display.CurrentScreen^.CheckFadeTo(nil,'MSG_QUIT_USDX'); - end - else // When asking for exit is disabled then simply exit - begin - Display.Fade := 0; - Display.NextScreenWithCheck := nil; - Display.CheckOK := True; - end; - end; - - end; - end; - { - SDL_JOYAXISMOTION: - begin - // not implemented - end; - } - SDL_JOYBUTTONDOWN: - begin - // not implemented - end; - end; // Case - end; // While -end; - -function GetTimeForBeats(BPM, Beats: real): real; -begin - Result := 60 / BPM * Beats; -end; - -function GetBeats(BPM, msTime: real): real; -begin - Result := BPM * msTime / 60; -end; - -procedure GetMidBeatSub(BPMNum: integer; var Time: real; var CurBeat: real); -var - NewTime: real; -begin - if High(CurrentSong.BPM) = BPMNum then - begin - // last BPM - CurBeat := CurrentSong.BPM[BPMNum].StartBeat + GetBeats(CurrentSong.BPM[BPMNum].BPM, Time); - Time := 0; - end - else - begin - // not last BPM - // count how much time is it for start of the new BPM and store it in NewTime - NewTime := GetTimeForBeats(CurrentSong.BPM[BPMNum].BPM, CurrentSong.BPM[BPMNum+1].StartBeat - CurrentSong.BPM[BPMNum].StartBeat); - - // compare it to remaining time - if (Time - NewTime) > 0 then - begin - // there is still remaining time - CurBeat := CurrentSong.BPM[BPMNum].StartBeat; - Time := Time - NewTime; - end - else - begin - // there is no remaining time - CurBeat := CurrentSong.BPM[BPMNum].StartBeat + GetBeats(CurrentSong.BPM[BPMNum].BPM, Time); - Time := 0; - end; // if - end; // if -end; - -function GetMidBeat(Time: real): real; -var - CurBeat: real; - CurBPM: integer; -// TopBeat: real; -// TempBeat: real; -// TempTime: real; -begin - Result := 0; - if Length(CurrentSong.BPM) = 1 then - Result := Time * CurrentSong.BPM[0].BPM / 60; - - (* 2 BPMs *) -{ if Length(CurrentSong.BPM) > 1 then begin - (* new system *) - CurBeat := 0; - TopBeat := GetBeats(CurrentSong.BPM[0].BPM, Time); - if TopBeat > CurrentSong.BPM[1].StartBeat then begin - // analyze second BPM - Time := Time - GetTimeForBeats(CurrentSong.BPM[0].BPM, CurrentSong.BPM[1].StartBeat - CurBeat); - CurBeat := CurrentSong.BPM[1].StartBeat; - TopBeat := GetBeats(CurrentSong.BPM[1].BPM, Time); - Result := CurBeat + TopBeat; - - end - else - begin - (* pierwszy przedzial *) - Result := TopBeat; - end; - end;} - - (* more BPMs *) - if Length(CurrentSong.BPM) > 1 then - begin - CurBeat := 0; - CurBPM := 0; - while (Time > 0) do - begin - GetMidBeatSub(CurBPM, Time, CurBeat); - Inc(CurBPM); - end; - - Result := CurBeat; - end; -end; - -function GetTimeFromBeat(Beat: integer): real; -var - CurBPM: integer; -begin - Result := 0; - if Length(CurrentSong.BPM) = 1 then - Result := CurrentSong.GAP / 1000 + Beat * 60 / CurrentSong.BPM[0].BPM; - - (* more BPMs *) - if Length(CurrentSong.BPM) > 1 then - begin - Result := CurrentSong.GAP / 1000; - CurBPM := 0; - while (CurBPM <= High(CurrentSong.BPM)) and - (Beat > CurrentSong.BPM[CurBPM].StartBeat) do - begin - if (CurBPM < High(CurrentSong.BPM)) and - (Beat >= CurrentSong.BPM[CurBPM+1].StartBeat) then - begin - // full range - Result := Result + (60 / CurrentSong.BPM[CurBPM].BPM) * - (CurrentSong.BPM[CurBPM+1].StartBeat - CurrentSong.BPM[CurBPM].StartBeat); - end; - - if (CurBPM = High(CurrentSong.BPM)) or - (Beat < CurrentSong.BPM[CurBPM+1].StartBeat) then - begin - // in the middle - Result := Result + (60 / CurrentSong.BPM[CurBPM].BPM) * - (Beat - CurrentSong.BPM[CurBPM].StartBeat); - end; - Inc(CurBPM); - end; - - { - while (Time > 0) do - begin - GetMidBeatSub(CurBPM, Time, CurBeat); - Inc(CurBPM); - end; - } - end; // if} -end; - -procedure Sing(Sender: TScreenSing); -var - Count: integer; - CountGr: integer; - CP: integer; - Done: real; - N: integer; -begin - LineState.CurrentTime := LineState.CurrentTime + TimeSkip; - - LineState.OldBeat := LineState.CurrentBeat; - LineState.MidBeat := GetMidBeat(LineState.CurrentTime - (CurrentSong.Gap{ + 90 I've forgotten for what it is}) / 1000); // new system with variable BPM in function - LineState.CurrentBeat := Floor(LineState.MidBeat); - -// LineState.OldHalf := LineState.AktHalf; -// LineState.MidHalf := LineState.MidBeat + 0.5; -// LineState.AktHalf := Floor(LineState.MidHalf); - - LineState.OldBeatC := LineState.CurrentBeatC; - LineState.MidBeatC := GetMidBeat(LineState.CurrentTime - (CurrentSong.Gap) / 1000); - LineState.CurrentBeatC := Floor(LineState.MidBeatC); - - LineState.OldBeatD := LineState.CurrentBeatD; - LineState.MidBeatD := -0.5+GetMidBeat(LineState.CurrentTime - (CurrentSong.Gap + 120 + 20) / 1000); // MidBeat with addition GAP - LineState.CurrentBeatD := Floor(LineState.MidBeatD); - LineState.FracBeatD := Frac(LineState.MidBeatD); - - // sentences routines - for CountGr := 0 to 0 do //High(Gracz) - begin; - CP := CountGr; - // ustawianie starej parts - LineState.OldLine := Lines[CP].Current; - - // wybieranie aktualnej parts - for Count := 0 to Lines[CP].High do - begin - if LineState.CurrentBeat >= Lines[CP].Line[Count].Start then - Lines[CP].Current := Count; - end; - - // czysczenie nut gracza, gdy to jest nowa plansza - // (optymizacja raz na halfbeat jest zla) - if Lines[CP].Current <> LineState.OldLine then - NewSentence(Sender); - - end; // for CountGr - - // wykonuje operacje raz na beat - if (LineState.CurrentBeat >= 0) and (LineState.OldBeat <> LineState.CurrentBeat) then - NewBeat(Sender); - - // make some operations on clicks - if {(LineState.CurrentBeatC >= 0) and }(LineState.OldBeatC <> LineState.CurrentBeatC) then - NewBeatC(Sender); - - // make some operations when detecting new voice pitch - if (LineState.CurrentBeatD >= 0) and (LineState.OldBeatD <> LineState.CurrentBeatD) then - NewBeatD(Sender); - - // wykonuje operacje w polowie beatu -// if (LineState.AktHalf >= 1) and (LineState.OldHalf <> LineState.AktHalf) then -// NewHalf; - - // plynnie przesuwa text - Done := 1; - for N := 0 to Lines[0].Line[Lines[0].Current].HighNote do - begin - if (Lines[0].Line[Lines[0].Current].Note[N].Start <= LineState.MidBeat) and - (Lines[0].Line[Lines[0].Current].Note[N].Start + Lines[0].Line[Lines[0].Current].Note[N].Length >= LineState.MidBeat) then - begin - Done := (LineState.MidBeat - Lines[0].Line[Lines[0].Current].Note[N].Start) / (Lines[0].Line[Lines[0].Current].Note[N].Length); - end; - end; - - N := Lines[0].Line[Lines[0].Current].HighNote; - - // wylacza ostatnia nute po przejsciu - {// todo: Lyrics - if (Ini.LyricsEffect = 1) and (Done = 1) and - (LineState.MidBeat > Lines[0].Line[Lines[0].Current].Note[N].Start + Lines[0].Line[Lines[0].Current].Note[N].Length) - then Sender.LyricMain.Selected := -1; - - if Done > 1 then Done := 1; - Sender.LyricMain.Done := Done; } - - // use Done with LCD -{ with ScreenSing do begin - if LyricMain.Selected >= 0 then begin - LCD.MoveCursor(1, LyricMain.SelectedLetter + Round((LyricMain.SelectedLength-1) * Done)); - LCD.ShowCursor; - end; - end;} - - -end; - -procedure NewSentence(Sender: TScreenSing); -var -G: Integer; -begin - // czyszczenie nut graczy - for G := 0 to High(Player) do - begin - Player[G].IlNut := 0; - Player[G].HighNote := -1; - SetLength(Player[G].Note, 0); - end; - - // Add Words to Lyrics - with Sender do - begin - {LyricMain.AddCzesc(Lines[0].Current); - if Lines[0].Current < Lines[0].High then - LyricSub.AddCzesc(Lines[0].Current+1) - else - LyricSub.Clear;} - while (not Lyrics.LineinQueue) and (Lyrics.LineCounter <= High(Lines[0].Line)) do - Lyrics.AddLine(@Lines[0].Line[Lyrics.LineCounter]); - end; - - //Sender.UpdateLCD; - - //On Sentence Change... - Sender.onSentenceChange(Lines[0].Current); -end; - -procedure NewBeat(Sender: TScreenSing); -var - Count: integer; -// TempBeat: integer; -begin - // ustawia zaznaczenie tekstu -// SingScreen.LyricMain.Selected := -1; - for Count := 0 to Lines[0].Line[Lines[0].Current].HighNote do - if (Lines[0].Line[Lines[0].Current].Note[Count].Start = LineState.CurrentBeat) then - begin - // operates on currently beated note - //Todo: Lyrics - //Sender.LyricMain.Selected := Count; - -// LCD.MoveCursor(1, ScreenSing.LyricMain.SelectedLetter); -// LCD.ShowCursor; - - //LCD.MoveCursorBR(Sender.LyricMain.SelectedLetter); - //LCD.ShowCursor; - end; -end; - -procedure NewBeatC; -var - Count: integer; -// LPT_1: integer; -// LPT_2: integer; -begin -// LPT_1 := 1; -// LPT_2 := 1; - - // beat click - if (Ini.BeatClick = 1) and ((LineState.CurrentBeatC + Lines[0].Resolution + Lines[0].NotesGAP) mod Lines[0].Resolution = 0) then - AudioPlayback.PlaySound(SoundLib.Click); - - // debug system on LPT - if ((LineState.CurrentBeatC + Lines[0].Resolution + Lines[0].NotesGAP) mod Lines[0].Resolution = 0) then - begin - //LPT_1 := 0; -// Light.LightOne(0, 150); - - (* - Light.LightOne(1, 200); // beat light - if ParamStr(1) = '-doublelights' then - Light.LightOne(0, 200); // beat light - *) - - -{ if ((LineState.CurrentBeatC + Lines[0].Resolution + Lines[0].NotesGAP) mod (Lines[0].Resolution * 2) = 0) then - Light.LightOne(0, 150) - else - Light.LightOne(1, 150)} - end; - - for Count := 0 to Lines[0].Line[Lines[0].Current].HighNote do - begin - if (Lines[0].Line[Lines[0].Current].Note[Count].Start = LineState.CurrentBeatC) then - begin - // click assist - if Ini.ClickAssist = 1 then - AudioPlayback.PlaySound(SoundLib.Click); - - //LPT_2 := 0; - (* - if ParamStr(1) <> '-doublelights' then - Light.LightOne(0, 150); //125 - *) - - // drum machine - (* - TempBeat := LineState.CurrentBeat;// + 2; - if (TempBeat mod 8 = 0) then Music.PlayDrum; - if (TempBeat mod 8 = 4) then Music.PlayClap; -// if (TempBeat mod 4 = 2) then Music.PlayHihat; - if (TempBeat mod 4 <> 0) then Music.PlayHihat; - *) - end; - end; - - {$IFDEF UseSerialPort} - // PortWriteB($378, LPT_1 + LPT_2 * 2); // 0 zapala - {$ENDIF} -end; - -procedure NewBeatD(Sender: TScreenSing); -begin - NewNote(Sender); -end; - -//procedure NewHalf; -//begin -// NewNote; -//end; - -procedure NewNote(Sender: TScreenSing); +unit UMain; + +interface + +{$IFDEF FPC} + {$MODE Delphi} +{$ENDIF} + +{$I switches.inc} + +uses + SDL, + UGraphic, + UMusic, + URecord, + UTime, + SysUtils, + UDisplay, + UIni, + ULog, + ULyrics, + UScreenSing, + USong, + OpenGL12, + UThemes; + +type + TPlayer = record + Name: string; + + // Index in Teaminfo record + TeamID: Byte; + PlayerID: Byte; + + // Scores + Score: real; + ScoreLine: real; + ScoreGolden: real; + + ScoreI: integer; + ScoreLineI: integer; + ScoreGoldenI: integer; + ScoreTotalI: integer; + + // LineBonus Mod + ScoreLast: Real;//Last Line Score + + // PerfectLineTwinkle Mod (effect) + LastSentencePerfect: Boolean; + + //Meter: real; + + HighNote: integer; + IlNut: integer; + Note: array of record + Start: integer; + Length: integer; + Detekt: real; // accurate place, detected in the note + Tone: real; + Perfect: boolean; // true if the note matches the original one, lit the star + + // Half size Notes Patch + Hit: boolean; // true if the note Hits the Line + end; + end; + + +var + // Absolute Paths + GamePath: string; + SoundPath: string; + SongPath: string; + LogPath: string; + ThemePath: string; + SkinsPath: string; + ScreenshotsPath: string; + CoversPath: string; + LanguagesPath: string; + PluginPath: string; + VisualsPath: string; + PlayListPath: string; + + UserSongPath: string = ''; + UserCoversPath: string = ''; + UserPlaylistPath: string = ''; + + OGL: Boolean; + Done: Boolean; + Event: TSDL_event; + FileName: string; + Restart: boolean; + + // player and music info + Player: array of TPlayer; + PlayersPlay: integer; + + CurrentSong : TSong; + +procedure InitializePaths; + +Procedure Main; +procedure MainLoop; +procedure CheckEvents; +procedure Sing(Sender: TScreenSing); +procedure NewSentence(Sender: TScreenSing); +procedure NewBeat(Sender: TScreenSing); // executed when on then new beat +procedure NewBeatC(Sender: TScreenSing); // executed when on then new beat for click +procedure NewBeatD(Sender: TScreenSing); // executed when on then new beat for detection +//procedure NewHalf; // executed when in the half between beats +procedure NewNote(Sender: TScreenSing); // detect note +function GetMidBeat(Time: real): real; +function GetTimeFromBeat(Beat: integer): real; +procedure ClearScores(PlayerNum: integer); + +implementation + +uses + USongs, + UJoystick, + math, + UCommandLine, + ULanguage, + SDL_ttf, + USkins, + UCovers, + UCatCovers, + UDataBase, + UPlaylist, + UDLLManager, + UParty, + UConfig, + UCore, + UGraphicClasses, + UPluginDefs, + UPlatform; + + + + +procedure Main; +var + WndTitle: string; +begin + try + WndTitle := USDXVersionStr; + + if Platform.TerminateIfAlreadyRunning( {var} WndTitle) then + Exit; + + //------------------------------ + //StartUp - Create Classes and Load Files + //------------------------------ + USTime := TTime.Create; + + // Commandline Parameter Parser + Params := TCMDParams.Create; + + // Log + Benchmark + Log := TLog.Create; + Log.Title := WndTitle; + Log.Enabled := not Params.NoLog; + Log.BenchmarkStart(0); + + // Language + Log.BenchmarkStart(1); + Log.LogStatus('Initialize Paths', 'Initialization'); + InitializePaths; + Log.LogStatus('Load Language', 'Initialization'); + Language := TLanguage.Create; + + // Add Const Values: + Language.AddConst('US_VERSION', USDXVersionStr); + Log.BenchmarkEnd(1); + Log.LogBenchmark('Loading Language', 1); + + // SDL + Log.BenchmarkStart(1); + Log.LogStatus('Initialize SDL', 'Initialization'); + SDL_Init(SDL_INIT_VIDEO); + Log.BenchmarkEnd(1); + Log.LogBenchmark('Initializing SDL', 1); + + // SDL_ttf + Log.BenchmarkStart(1); + Log.LogStatus('Initialize SDL_ttf', 'Initialization'); + TTF_Init(); + Log.BenchmarkEnd(1); + Log.LogBenchmark('Initializing SDL_ttf', 1); + + // Skin + Log.BenchmarkStart(1); + Log.LogStatus('Loading Skin List', 'Initialization'); + Skin := TSkin.Create; + Log.BenchmarkEnd(1); + Log.LogBenchmark('Loading Skin List', 1); + + // Ini + Paths + Log.BenchmarkStart(1); + Log.LogStatus('Load Ini', 'Initialization'); + Ini := TIni.Create; + Ini.Load; + + //Load Languagefile + if (Params.Language <> -1) then + Language.ChangeLanguage(ILanguage[Params.Language]) + else + Language.ChangeLanguage(ILanguage[Ini.Language]); + + Log.BenchmarkEnd(1); + Log.LogBenchmark('Loading Ini', 1); + + // Sound + Log.BenchmarkStart(1); + Log.LogStatus('Initialize Sound', 'Initialization'); + InitializeSound(); + Log.BenchmarkEnd(1); + Log.LogBenchmark('Initializing Sound', 1); + + // Load Sound Settings from Ini + Log.BenchmarkStart(1); + Log.LogStatus('Load Sound Settings', 'Initialization'); + Ini.LoadSoundSettings; + Log.BenchmarkEnd(1); + Log.LogBenchmark('Load Sound Settings', 1); + + // Theme + Log.BenchmarkStart(1); + Log.LogStatus('Load Themes', 'Initialization'); + Theme := TTheme.Create(ThemePath + ITheme[Ini.Theme] + '.ini', Ini.Color); + Log.BenchmarkEnd(1); + Log.LogBenchmark('Loading Themes', 1); + + // Covers Cache + Log.BenchmarkStart(1); + Log.LogStatus('Creating Covers Cache', 'Initialization'); + Covers := TCovers.Create('covers.cache'); + Log.LogBenchmark('Loading Covers Cache Array', 1); + Log.BenchmarkStart(1); + + // Category Covers + Log.BenchmarkStart(1); + Log.LogStatus('Creating Category Covers Array', 'Initialization'); + CatCovers:= TCatCovers.Create; + Log.BenchmarkEnd(1); + Log.LogBenchmark('Loading Category Covers Array', 1); + + // Songs + //Log.BenchmarkStart(1); + Log.LogStatus('Creating Song Array', 'Initialization'); + Songs := TSongs.Create; + //Songs.LoadSongList; + + Log.LogStatus('Creating 2nd Song Array', 'Initialization'); + CatSongs := TCatSongs.Create; + + Log.BenchmarkEnd(1); + Log.LogBenchmark('Loading Songs', 1); + + // PluginManager + Log.BenchmarkStart(1); + Log.LogStatus('PluginManager', 'Initialization'); + DLLMan := TDLLMan.Create; // Load PluginList + Log.BenchmarkEnd(1); + Log.LogBenchmark('Loading PluginManager', 1); + + {// Party Mode Manager + Log.BenchmarkStart(1); + Log.LogStatus('PartySession Manager', 'Initialization'); + PartySession := TPartySession.Create; //Load PartySession + + Log.BenchmarkEnd(1); + Log.LogBenchmark('Loading PartySession Manager', 1); } + + // Graphics + Log.BenchmarkStart(1); + Log.LogStatus('Initialize 3D', 'Initialization'); + Initialize3D(WndTitle); + Log.BenchmarkEnd(1); + Log.LogBenchmark('Initializing 3D', 1); + + // Score Saving System + Log.BenchmarkStart(1); + Log.LogStatus('DataBase System', 'Initialization'); + DataBase := TDataBaseSystem.Create; + + if (Params.ScoreFile = '') then + DataBase.Init ('Ultrastar.db') + else + DataBase.Init (Params.ScoreFile); + + Log.BenchmarkEnd(1); + Log.LogBenchmark('Loading DataBase System', 1); + + // Playlist Manager + Log.BenchmarkStart(1); + Log.LogStatus('Playlist Manager', 'Initialization'); + PlaylistMan := TPlaylistManager.Create; + Log.BenchmarkEnd(1); + Log.LogBenchmark('Loading Playlist Manager', 1); + + // GoldenStarsTwinkleMod + Log.BenchmarkStart(1); + Log.LogStatus('Effect Manager', 'Initialization'); + GoldenRec := TEffectManager.Create; + Log.BenchmarkEnd(1); + Log.LogBenchmark('Loading Particel System', 1); + + // Joypad + if (Ini.Joypad = 1) OR (Params.Joypad) then + begin + Log.BenchmarkStart(1); + Log.LogStatus('Initialize Joystick', 'Initialization'); + Joy := TJoy.Create; + Log.BenchmarkEnd(1); + Log.LogBenchmark('Initializing Joystick', 1); + end; + + Log.BenchmarkEnd(0); + Log.LogBenchmark('Loading Time', 0); + + Log.LogError('Creating Core'); + {Core := TCore.Create( + USDXShortVersionStr, + MakeVersion(USDX_VERSION_MAJOR, + USDX_VERSION_MINOR, + USDX_VERSION_RELEASE, + chr(0)) + ); } + + Log.LogError('Running Core'); + //Core.Run; + + //------------------------------ + //Start- Mainloop + //------------------------------ + Log.LogStatus('Main Loop', 'Initialization'); + MainLoop; + + finally + //------------------------------ + //Finish Application + //------------------------------ + + // TODO: + // call an uninitialize routine for every initialize step + // or at least use the corresponding Free-Methods + + UnloadOpenGL; + //TTF_quit(); + SDL_Quit(); + + (* + {$ifdef WIN32} + if assigned(LCD) and (Ini.LPT = 1) then + LCD.Clear; + if assigned(Light) and (Ini.LPT = 2) then + Light.TurnOff; + {$endif} + *) + + if assigned(Log) then + begin + Log.LogStatus('Main Loop', 'Finished'); + Log.Free; + end; + end; +end; + +procedure MainLoop; +var + Delay: integer; +begin + Delay := 0; + SDL_EnableKeyRepeat(125, 125); + + CountSkipTime(); // JB - for some reason this seems to be needed when we use the SDL Timer functions. + while not Done do + begin + // joypad + if (Ini.Joypad = 1) or (Params.Joypad) then + Joy.Update; + + // keyboard events + CheckEvents; + + // display + done := not Display.Draw; + SwapBuffers; + + // light + //Light.Refresh; + + // delay + CountMidTime; + + Delay := Floor(1000 / 100 - 1000 * TimeMid); + + if Delay >= 1 then + SDL_Delay(Delay); // dynamic, maximum is 100 fps + + CountSkipTime; + + // reinitialization of graphics + if Restart then + begin + Reinitialize3D; + Restart := false; + end; + + end; +End; + +procedure CheckEvents; +begin + if Assigned(Display.NextScreen) then + Exit; + + while SDL_PollEvent( @event ) = 1 do + begin + case Event.type_ of + SDL_QUITEV: + begin + Display.Fade := 0; + Display.NextScreenWithCheck := nil; + Display.CheckOK := True; + end; + { + SDL_MOUSEBUTTONDOWN: + with Event.button Do + begin + if State = SDL_BUTTON_LEFT Then + begin + // + end; + end; + } + SDL_VIDEORESIZE: + begin + ScreenW := Event.resize.w; + ScreenH := Event.resize.h; + + screen := SDL_SetVideoMode(ScreenW, ScreenH, (Ini.Depth+1) * 16, SDL_OPENGL or SDL_RESIZABLE); + end; + SDL_KEYDOWN: + begin + // remap the "keypad enter" key to the "standard enter" key + if (Event.key.keysym.sym = SDLK_KP_ENTER) then + Event.key.keysym.sym := SDLK_RETURN; + + if (Event.key.keysym.sym = SDLK_F11) or + ((Event.key.keysym.sym = SDLK_RETURN) and + ((Event.key.keysym.modifier and KMOD_ALT) <> 0)) then // toggle full screen + begin + Ini.FullScreen := integer( not boolean( Ini.FullScreen ) ); + + if boolean( Ini.FullScreen ) then + begin + SDL_SetVideoMode(ScreenW, ScreenH, (Ini.Depth+1) * 16, SDL_OPENGL or SDL_FULLSCREEN); + SDL_ShowCursor(0); + end + else + begin + SDL_SetVideoMode(ScreenW, ScreenH, (Ini.Depth+1) * 16, SDL_OPENGL or SDL_RESIZABLE); + SDL_ShowCursor(1); + end; + + glViewPort(0, 0, ScreenW, ScreenH); + end + // ScreenShot hack. If Print is pressed-> Make screenshot and Save to Screenshots Path + else if (Event.key.keysym.sym = SDLK_SYSREQ) or (Event.key.keysym.sym = SDLK_PRINT) then + Display.ScreenShot + // popup hack... if there is a visible popup then let it handle input instead of underlying screen + // shoud be done in a way to be sure the topmost popup has preference (maybe error, then check) + else if (ScreenPopupError <> nil) and (ScreenPopupError.Visible) then + done := not ScreenPopupError.ParseInput(Event.key.keysym.sym, WideChar(Event.key.keysym.unicode), True) + else if (ScreenPopupCheck <> nil) and (ScreenPopupCheck.Visible) then + done := not ScreenPopupCheck.ParseInput(Event.key.keysym.sym, WideChar(Event.key.keysym.unicode), True) + // end of popup hack + else + begin + // check for Screen want to Exit + done := not Display.CurrentScreen^.ParseInput(Event.key.keysym.sym, WideChar(Event.key.keysym.unicode), True); + + // If Screen wants to Exit + if done then + begin + // If Question Option is enabled then Show Exit Popup + if (Ini.AskbeforeDel = 1) then + begin + Display.CurrentScreen^.CheckFadeTo(nil,'MSG_QUIT_USDX'); + end + else // When asking for exit is disabled then simply exit + begin + Display.Fade := 0; + Display.NextScreenWithCheck := nil; + Display.CheckOK := True; + end; + end; + + end; + end; + { + SDL_JOYAXISMOTION: + begin + // not implemented + end; + } + SDL_JOYBUTTONDOWN: + begin + // not implemented + end; + end; // Case + end; // While +end; + +function GetTimeForBeats(BPM, Beats: real): real; +begin + Result := 60 / BPM * Beats; +end; + +function GetBeats(BPM, msTime: real): real; +begin + Result := BPM * msTime / 60; +end; + +procedure GetMidBeatSub(BPMNum: integer; var Time: real; var CurBeat: real); +var + NewTime: real; +begin + if High(CurrentSong.BPM) = BPMNum then + begin + // last BPM + CurBeat := CurrentSong.BPM[BPMNum].StartBeat + GetBeats(CurrentSong.BPM[BPMNum].BPM, Time); + Time := 0; + end + else + begin + // not last BPM + // count how much time is it for start of the new BPM and store it in NewTime + NewTime := GetTimeForBeats(CurrentSong.BPM[BPMNum].BPM, CurrentSong.BPM[BPMNum+1].StartBeat - CurrentSong.BPM[BPMNum].StartBeat); + + // compare it to remaining time + if (Time - NewTime) > 0 then + begin + // there is still remaining time + CurBeat := CurrentSong.BPM[BPMNum].StartBeat; + Time := Time - NewTime; + end + else + begin + // there is no remaining time + CurBeat := CurrentSong.BPM[BPMNum].StartBeat + GetBeats(CurrentSong.BPM[BPMNum].BPM, Time); + Time := 0; + end; // if + end; // if +end; + +function GetMidBeat(Time: real): real; +var + CurBeat: real; + CurBPM: integer; +// TopBeat: real; +// TempBeat: real; +// TempTime: real; +begin + Result := 0; + if Length(CurrentSong.BPM) = 1 then + Result := Time * CurrentSong.BPM[0].BPM / 60; + + (* 2 BPMs *) +{ if Length(CurrentSong.BPM) > 1 then begin + (* new system *) + CurBeat := 0; + TopBeat := GetBeats(CurrentSong.BPM[0].BPM, Time); + if TopBeat > CurrentSong.BPM[1].StartBeat then begin + // analyze second BPM + Time := Time - GetTimeForBeats(CurrentSong.BPM[0].BPM, CurrentSong.BPM[1].StartBeat - CurBeat); + CurBeat := CurrentSong.BPM[1].StartBeat; + TopBeat := GetBeats(CurrentSong.BPM[1].BPM, Time); + Result := CurBeat + TopBeat; + + end + else + begin + (* pierwszy przedzial *) + Result := TopBeat; + end; + end;} + + (* more BPMs *) + if Length(CurrentSong.BPM) > 1 then + begin + CurBeat := 0; + CurBPM := 0; + while (Time > 0) do + begin + GetMidBeatSub(CurBPM, Time, CurBeat); + Inc(CurBPM); + end; + + Result := CurBeat; + end; +end; + +function GetTimeFromBeat(Beat: integer): real; +var + CurBPM: integer; +begin + Result := 0; + if Length(CurrentSong.BPM) = 1 then + Result := CurrentSong.GAP / 1000 + Beat * 60 / CurrentSong.BPM[0].BPM; + + (* more BPMs *) + if Length(CurrentSong.BPM) > 1 then + begin + Result := CurrentSong.GAP / 1000; + CurBPM := 0; + while (CurBPM <= High(CurrentSong.BPM)) and + (Beat > CurrentSong.BPM[CurBPM].StartBeat) do + begin + if (CurBPM < High(CurrentSong.BPM)) and + (Beat >= CurrentSong.BPM[CurBPM+1].StartBeat) then + begin + // full range + Result := Result + (60 / CurrentSong.BPM[CurBPM].BPM) * + (CurrentSong.BPM[CurBPM+1].StartBeat - CurrentSong.BPM[CurBPM].StartBeat); + end; + + if (CurBPM = High(CurrentSong.BPM)) or + (Beat < CurrentSong.BPM[CurBPM+1].StartBeat) then + begin + // in the middle + Result := Result + (60 / CurrentSong.BPM[CurBPM].BPM) * + (Beat - CurrentSong.BPM[CurBPM].StartBeat); + end; + Inc(CurBPM); + end; + + { + while (Time > 0) do + begin + GetMidBeatSub(CurBPM, Time, CurBeat); + Inc(CurBPM); + end; + } + end; // if} +end; + +procedure Sing(Sender: TScreenSing); +var + Count: integer; + CountGr: integer; + CP: integer; + Done: real; + N: integer; +begin + LineState.CurrentTime := LineState.CurrentTime + TimeSkip; + + LineState.OldBeat := LineState.CurrentBeat; + LineState.MidBeat := GetMidBeat(LineState.CurrentTime - (CurrentSong.Gap{ + 90 I've forgotten for what it is}) / 1000); // new system with variable BPM in function + LineState.CurrentBeat := Floor(LineState.MidBeat); + +// LineState.OldHalf := LineState.AktHalf; +// LineState.MidHalf := LineState.MidBeat + 0.5; +// LineState.AktHalf := Floor(LineState.MidHalf); + + LineState.OldBeatC := LineState.CurrentBeatC; + LineState.MidBeatC := GetMidBeat(LineState.CurrentTime - (CurrentSong.Gap) / 1000); + LineState.CurrentBeatC := Floor(LineState.MidBeatC); + + LineState.OldBeatD := LineState.CurrentBeatD; + LineState.MidBeatD := -0.5+GetMidBeat(LineState.CurrentTime - (CurrentSong.Gap + 120 + 20) / 1000); // MidBeat with addition GAP + LineState.CurrentBeatD := Floor(LineState.MidBeatD); + LineState.FracBeatD := Frac(LineState.MidBeatD); + + // sentences routines + for CountGr := 0 to 0 do //High(Gracz) + begin; + CP := CountGr; + // ustawianie starej parts + LineState.OldLine := Lines[CP].Current; + + // wybieranie aktualnej parts + for Count := 0 to Lines[CP].High do + begin + if LineState.CurrentBeat >= Lines[CP].Line[Count].Start then + Lines[CP].Current := Count; + end; + + // czysczenie nut gracza, gdy to jest nowa plansza + // (optymizacja raz na halfbeat jest zla) + if Lines[CP].Current <> LineState.OldLine then + NewSentence(Sender); + + end; // for CountGr + + // wykonuje operacje raz na beat + if (LineState.CurrentBeat >= 0) and (LineState.OldBeat <> LineState.CurrentBeat) then + NewBeat(Sender); + + // make some operations on clicks + if {(LineState.CurrentBeatC >= 0) and }(LineState.OldBeatC <> LineState.CurrentBeatC) then + NewBeatC(Sender); + + // make some operations when detecting new voice pitch + if (LineState.CurrentBeatD >= 0) and (LineState.OldBeatD <> LineState.CurrentBeatD) then + NewBeatD(Sender); + + // wykonuje operacje w polowie beatu +// if (LineState.AktHalf >= 1) and (LineState.OldHalf <> LineState.AktHalf) then +// NewHalf; + + // plynnie przesuwa text + Done := 1; + for N := 0 to Lines[0].Line[Lines[0].Current].HighNote do + begin + if (Lines[0].Line[Lines[0].Current].Note[N].Start <= LineState.MidBeat) and + (Lines[0].Line[Lines[0].Current].Note[N].Start + Lines[0].Line[Lines[0].Current].Note[N].Length >= LineState.MidBeat) then + begin + Done := (LineState.MidBeat - Lines[0].Line[Lines[0].Current].Note[N].Start) / (Lines[0].Line[Lines[0].Current].Note[N].Length); + end; + end; + + N := Lines[0].Line[Lines[0].Current].HighNote; + + // wylacza ostatnia nute po przejsciu + {// todo: Lyrics + if (Ini.LyricsEffect = 1) and (Done = 1) and + (LineState.MidBeat > Lines[0].Line[Lines[0].Current].Note[N].Start + Lines[0].Line[Lines[0].Current].Note[N].Length) + then Sender.LyricMain.Selected := -1; + + if Done > 1 then Done := 1; + Sender.LyricMain.Done := Done; } + + // use Done with LCD +{ with ScreenSing do begin + if LyricMain.Selected >= 0 then begin + LCD.MoveCursor(1, LyricMain.SelectedLetter + Round((LyricMain.SelectedLength-1) * Done)); + LCD.ShowCursor; + end; + end;} + + +end; + +procedure NewSentence(Sender: TScreenSing); +var +G: Integer; +begin + // czyszczenie nut graczy + for G := 0 to High(Player) do + begin + Player[G].IlNut := 0; + Player[G].HighNote := -1; + SetLength(Player[G].Note, 0); + end; + + // Add Words to Lyrics + with Sender do + begin + {LyricMain.AddCzesc(Lines[0].Current); + if Lines[0].Current < Lines[0].High then + LyricSub.AddCzesc(Lines[0].Current+1) + else + LyricSub.Clear;} + while (not Lyrics.LineinQueue) and (Lyrics.LineCounter <= High(Lines[0].Line)) do + Lyrics.AddLine(@Lines[0].Line[Lyrics.LineCounter]); + end; + + //Sender.UpdateLCD; + + //On Sentence Change... + Sender.onSentenceChange(Lines[0].Current); +end; + +procedure NewBeat(Sender: TScreenSing); +var + Count: integer; +// TempBeat: integer; +begin + // ustawia zaznaczenie tekstu +// SingScreen.LyricMain.Selected := -1; + for Count := 0 to Lines[0].Line[Lines[0].Current].HighNote do + if (Lines[0].Line[Lines[0].Current].Note[Count].Start = LineState.CurrentBeat) then + begin + // operates on currently beated note + //Todo: Lyrics + //Sender.LyricMain.Selected := Count; + +// LCD.MoveCursor(1, ScreenSing.LyricMain.SelectedLetter); +// LCD.ShowCursor; + + //LCD.MoveCursorBR(Sender.LyricMain.SelectedLetter); + //LCD.ShowCursor; + end; +end; + +procedure NewBeatC; +var + Count: integer; +// LPT_1: integer; +// LPT_2: integer; +begin +// LPT_1 := 1; +// LPT_2 := 1; + + // beat click + if (Ini.BeatClick = 1) and ((LineState.CurrentBeatC + Lines[0].Resolution + Lines[0].NotesGAP) mod Lines[0].Resolution = 0) then + AudioPlayback.PlaySound(SoundLib.Click); + + // debug system on LPT + if ((LineState.CurrentBeatC + Lines[0].Resolution + Lines[0].NotesGAP) mod Lines[0].Resolution = 0) then + begin + //LPT_1 := 0; +// Light.LightOne(0, 150); + + (* + Light.LightOne(1, 200); // beat light + if ParamStr(1) = '-doublelights' then + Light.LightOne(0, 200); // beat light + *) + + +{ if ((LineState.CurrentBeatC + Lines[0].Resolution + Lines[0].NotesGAP) mod (Lines[0].Resolution * 2) = 0) then + Light.LightOne(0, 150) + else + Light.LightOne(1, 150)} + end; + + for Count := 0 to Lines[0].Line[Lines[0].Current].HighNote do + begin + if (Lines[0].Line[Lines[0].Current].Note[Count].Start = LineState.CurrentBeatC) then + begin + // click assist + if Ini.ClickAssist = 1 then + AudioPlayback.PlaySound(SoundLib.Click); + + //LPT_2 := 0; + (* + if ParamStr(1) <> '-doublelights' then + Light.LightOne(0, 150); //125 + *) + + // drum machine + (* + TempBeat := LineState.CurrentBeat;// + 2; + if (TempBeat mod 8 = 0) then Music.PlayDrum; + if (TempBeat mod 8 = 4) then Music.PlayClap; +// if (TempBeat mod 4 = 2) then Music.PlayHihat; + if (TempBeat mod 4 <> 0) then Music.PlayHihat; + *) + end; + end; + + {$IFDEF UseSerialPort} + // PortWriteB($378, LPT_1 + LPT_2 * 2); // 0 zapala + {$ENDIF} +end; + +procedure NewBeatD(Sender: TScreenSing); +begin + NewNote(Sender); +end; + +procedure NewNote(Sender: TScreenSing); var CP: integer; // current player S: integer; // sentence @@ -1049,74 +1044,73 @@ begin end; end; -end; - -procedure ClearScores(PlayerNum: integer); -begin - Player[PlayerNum].Score := 0; - Player[PlayerNum].ScoreI := 0; - Player[PlayerNum].ScoreLine := 0; - Player[PlayerNum].ScoreLineI := 0; - Player[PlayerNum].ScoreGolden := 0; - Player[PlayerNum].ScoreGoldenI := 0; - Player[PlayerNum].ScoreTotalI := 0; -end; - -//-------------------- -// Function sets all Absolute Paths e.g. Song Path and makes sure the Directorys exist -//-------------------- -procedure InitializePaths; - - // Initialize a Path Variable - // After Setting Paths, make sure that Paths exist - function initialize_path( out aPathVar : string; const aLocation : string ): boolean; - var - lWriteable: Boolean; - lAttrib : integer; - begin - lWriteable := false; - aPathVar := aLocation; - - // Make sure the directory is needex - ForceDirectories(aPathVar); - - if DirectoryExists(aPathVar) then - begin - lAttrib := fileGetAttr(aPathVar); - - lWriteable := (lAttrib and faDirectory <> 0) and - not (lAttrib and faReadOnly <> 0) - end; - - if not lWriteable then - Log.LogError('Error: Dir ('+ aLocation +') is Readonly'); - - result := lWriteable; - end; - -begin - initialize_path( LogPath , Platform.GetLogPath ); - initialize_path( SoundPath , Platform.GetGameSharedPath + 'Sounds' + PathDelim ); - initialize_path( ThemePath , Platform.GetGameSharedPath + 'Themes' + PathDelim ); - initialize_path( SkinsPath , Platform.GetGameSharedPath + 'Skins' + PathDelim ); - initialize_path( LanguagesPath , Platform.GetGameSharedPath + 'Languages' + PathDelim ); - initialize_path( PluginPath , Platform.GetGameSharedPath + 'Plugins' + PathDelim ); - initialize_path( VisualsPath , Platform.GetGameSharedPath + 'Visuals' + PathDelim ); - - initialize_path( ScreenshotsPath , Platform.GetGameUserPath + 'Screenshots' + PathDelim ); - - // Users Song Path .... - initialize_path( UserSongPath , Platform.GetGameUserPath + 'Songs' + PathDelim ); - initialize_path( UserCoversPath , Platform.GetGameUserPath + 'Covers' + PathDelim ); - initialize_path( UserPlaylistPath , Platform.GetGameUserPath + 'Playlists' + PathDelim ); - - // Shared Song Path .... - initialize_path( SongPath , Platform.GetGameSharedPath + 'Songs' + PathDelim ); - initialize_path( CoversPath , Platform.GetGameSharedPath + 'Covers' + PathDelim ); - initialize_path( PlaylistPath , Platform.GetGameSharedPath + 'Playlists' + PathDelim ); - - DecimalSeparator := '.'; -end; - -end. - +end; + +procedure ClearScores(PlayerNum: integer); +begin + Player[PlayerNum].Score := 0; + Player[PlayerNum].ScoreI := 0; + Player[PlayerNum].ScoreLine := 0; + Player[PlayerNum].ScoreLineI := 0; + Player[PlayerNum].ScoreGolden := 0; + Player[PlayerNum].ScoreGoldenI := 0; + Player[PlayerNum].ScoreTotalI := 0; +end; + +//-------------------- +// Function sets all Absolute Paths e.g. Song Path and makes sure the Directorys exist +//-------------------- +procedure InitializePaths; + + // Initialize a Path Variable + // After Setting Paths, make sure that Paths exist + function initialize_path( out aPathVar : string; const aLocation : string ): boolean; + var + lWriteable: Boolean; + lAttrib : integer; + begin + lWriteable := false; + aPathVar := aLocation; + + // Make sure the directory is needex + ForceDirectories(aPathVar); + + if DirectoryExists(aPathVar) then + begin + lAttrib := fileGetAttr(aPathVar); + + lWriteable := (lAttrib and faDirectory <> 0) and + not (lAttrib and faReadOnly <> 0) + end; + + if not lWriteable then + Log.LogError('Error: Dir ('+ aLocation +') is Readonly'); + + result := lWriteable; + end; + +begin + initialize_path( LogPath , Platform.GetLogPath ); + initialize_path( SoundPath , Platform.GetGameSharedPath + 'Sounds' + PathDelim ); + initialize_path( ThemePath , Platform.GetGameSharedPath + 'Themes' + PathDelim ); + initialize_path( SkinsPath , Platform.GetGameSharedPath + 'Skins' + PathDelim ); + initialize_path( LanguagesPath , Platform.GetGameSharedPath + 'Languages' + PathDelim ); + initialize_path( PluginPath , Platform.GetGameSharedPath + 'Plugins' + PathDelim ); + initialize_path( VisualsPath , Platform.GetGameSharedPath + 'Visuals' + PathDelim ); + + initialize_path( ScreenshotsPath , Platform.GetGameUserPath + 'Screenshots' + PathDelim ); + + // Users Song Path .... + initialize_path( UserSongPath , Platform.GetGameUserPath + 'Songs' + PathDelim ); + initialize_path( UserCoversPath , Platform.GetGameUserPath + 'Covers' + PathDelim ); + initialize_path( UserPlaylistPath , Platform.GetGameUserPath + 'Playlists' + PathDelim ); + + // Shared Song Path .... + initialize_path( SongPath , Platform.GetGameSharedPath + 'Songs' + PathDelim ); + initialize_path( CoversPath , Platform.GetGameSharedPath + 'Covers' + PathDelim ); + initialize_path( PlaylistPath , Platform.GetGameSharedPath + 'Playlists' + PathDelim ); + + DecimalSeparator := '.'; +end; + +end. diff --git a/Game/Code/Classes/USongs.pas b/Game/Code/Classes/USongs.pas index 87497b17..554a6e1e 100644 --- a/Game/Code/Classes/USongs.pas +++ b/Game/Code/Classes/USongs.pas @@ -224,7 +224,7 @@ begin CatCovers.Load; if assigned( Covers ) then - Covers.Load; + Covers.Load('covers.cache'); if assigned(ScreenSong) then begin diff --git a/Game/Code/Screens/UScreenSong.pas b/Game/Code/Screens/UScreenSong.pas index b26e1569..15c18a53 100644 --- a/Game/Code/Screens/UScreenSong.pas +++ b/Game/Code/Screens/UScreenSong.pas @@ -794,7 +794,7 @@ var Pet: integer; Label CreateSongButtons; begin - if (length(CatSongs.Song) > 0) then + {if (length(CatSongs.Song) > 0) then begin //Set Length of Button Array one Time Instead of one time for every Song SetButtonLength(Length(CatSongs.Song)); @@ -807,30 +807,30 @@ begin // new Texture.Limit := 512;// 256 0.4.2 value, 512 in 0.5.0 - if not FileExists({CatSongs.Song[Pet].Path + }CatSongs.Song[Pet].Cover) then + if not FileExists({CatSongs.Song[Pet].Path + }{CatSongs.Song[Pet].Cover) then CatSongs.Song[Pet].Cover := ''; // 0.5.0: if cover not found then show 'no cover' // to - do : new Song management if CatSongs.Song[Pet].Cover = '' then AddButton(300 + Pet*250, 140, 200, 200, Skin.GetTextureFileName('SongCover'), TEXTURE_TYPE_PLAIN, Theme.Song.Cover.Reflections) else begin // cache texture if there is a need to this - if not Covers.CoverExists({CatSongs.Song[Pet].Path + }CatSongs.Song[Pet].Cover) then + if not Covers.CoverExists({CatSongs.Song[Pet].Path + }{CatSongs.Song[Pet].Cover) then begin Texture.CreateCacheMipmap := true; - Texture.GetTexture({CatSongs.Song[Pet].Path + }CatSongs.Song[Pet].Cover, TEXTURE_TYPE_PLAIN, true); // preloads textures and creates cache mipmap + Texture.GetTexture({CatSongs.Song[Pet].Path + }{CatSongs.Song[Pet].Cover, TEXTURE_TYPE_PLAIN, true); // preloads textures and creates cache mipmap Texture.CreateCacheMipmap := false; // puts this texture to the cache file - Covers.AddCover({CatSongs.Song[Pet].Path + }CatSongs.Song[Pet].Cover); + Covers.AddCover({CatSongs.Song[Pet].Path + }{CatSongs.Song[Pet].Cover); // unload full size texture - Texture.UnloadTexture({CatSongs.Song[Pet].Path + }CatSongs.Song[Pet].Cover, TEXTURE_TYPE_PLAIN, false); + Texture.UnloadTexture({CatSongs.Song[Pet].Path + }{CatSongs.Song[Pet].Cover, TEXTURE_TYPE_PLAIN, false); // we should also add mipmap texture by calling createtexture and use mipmap cache as data source end; // and now load it from cache file (small place for the optimization by eliminating reading it from file, but not here) - AddButton(300 + Pet*250, 140, 200, 200,{ CatSongs.Song[Pet].Path + }CatSongs.Song[Pet].Cover, TEXTURE_TYPE_PLAIN, Theme.Song.Cover.Reflections); + AddButton(300 + Pet*250, 140, 200, 200,{ CatSongs.Song[Pet].Path + }{CatSongs.Song[Pet].Cover, TEXTURE_TYPE_PLAIN, Theme.Song.Cover.Reflections); end; Texture.Limit := 1024*1024; I := -1; @@ -852,14 +852,14 @@ begin try AddButton(300 + Pet*250, 140, 200, 200, '', TEXTURE_TYPE_PLAIN, Theme.Song.Cover.Reflections); except - {$IFDEF MSWINDOWS} + {$IFDEF MSWINDOWS}{ Messagebox(0, PChar('No Cover Image is damage. Could not Workaround Song Loading, Ultrastar will exit now.'), PChar(Language.Translate('US_VERSION')), MB_ICONERROR or MB_OK); - {$ELSE} + {$ELSE} { // TODO : JB_linux - better handle this message and display to user.. writeln( 'Cover Image is damaged. Could not Workaround Song Loading, Ultrastar will exit now.'); Log.LogError( 'No Cover Image is damage. Could not Workaround Song Loading, Ultrastar will exit now.' ); - {$ENDIF} + {$ENDIF} { Halt; end; I := Pet + 1; @@ -869,7 +869,7 @@ begin if (I <> -1) then GoTo CreateSongButtons; - end; + end; } end; procedure TScreenSong.SetScroll; -- cgit v1.2.3