unit UFiles; interface {$IFDEF FPC} {$MODE Delphi} {$ENDIF} {$I switches.inc} uses SysUtils, ULog, UMusic, USongs, USong; //procedure InitializePaths; //Function sets All Absolute Paths eg. for Songs //function ReadTXTHeader(var Song: TSong): boolean; //Reads Standard TXT Header //function AnalyseFile(var Song: TSong): boolean; //Analyse Song File and Read Header //procedure ClearSong(var Song: TSong); //Clears Song Header values //Methodes Loading and Saving Songfiles procedure ResetSingTemp; //procedure ParseNote(NrCzesci: integer; TypeP: char; StartP, DurationP, NoteP: integer; LyricS: string); //procedure NewSentence(NrCzesciP: integer; Param1, Param2: integer); //function LoadSong(Name: string): boolean; function SaveSong(Song: TSong; Czesc: TCzesci; Name: string; Relative: boolean): boolean; var SongFile: TextFile; // all procedures in this unit operates on this file FileLineNo: integer; //Line which is readed at Last, for error reporting // variables available for all procedures Base : array[0..1] of integer; Rel : array[0..1] of integer; Mult : integer = 1; MultBPM : integer = 4; implementation uses TextGL, UIni, UPlatform, UMain; (* //-------------------- <<<<<<< .mine ======= // Clears Song Header values //-------------------- procedure ClearSong(var Song: TSong); begin //Main Information Song.Title := ''; Song.Artist := ''; //Sortings: Song.Genre := 'Unknown'; Song.Edition := 'Unknown'; Song.Language := 'Unknown'; //Language Patch //Required Information Song.Mp3 := ''; {$IFDEF FPC} setlength( Song.BPM, 0 ); {$ELSE} Song.BPM := 0; {$ENDIF} Song.GAP := 0; Song.Start := 0; Song.Finish := 0; //Additional Information Song.Background := ''; Song.Cover := ''; Song.Video := ''; Song.VideoGAP := 0; Song.NotesGAP := 0; Song.Resolution := 4; Song.Creator := ''; end; //-------------------- // Reads Standard TXT Header //-------------------- function ReadTXTHeader(var Song: TSong): boolean; var Line, Identifier, Value: String; Temp: word; Done: byte; begin Result := true; Done := 0; //Read first Line ReadLn (SongFile, Line); if (Length(Line)<=0) then begin Log.LogError('File Starts with Empty Line: ' + Song.FileName); Result := False; Exit; end; //Read Lines while Line starts with # While (Length(Line) = 0) OR (Line[1] = '#') do begin //Increase Line Number Inc (FileLineNo); Temp := Pos(':', Line); //Line has a Seperator-> Headerline if (Temp <> 0) then begin //Read Identifier and Value Identifier := Uppercase(Trim(Copy(Line, 2, Temp - 2))); //Uppercase is for Case Insensitive Checks Value := Trim(Copy(Line, Temp + 1,Length(Line) - Temp)); //Check the Identifier (If Value is given) if (Length(Value) <> 0) then begin {$IFDEF DARWIN} if ((Identifier = 'MP3') or (Identifier = 'COVER') or (Identifier = 'BACKGROUND') or (Identifier = 'VIDEO')) then begin // Filenames on OS X must be UTF8: Value := Utf8Encode(Value); end; {$ENDIF} //----------- //Required Attributes //----------- //Title if (Identifier = 'TITLE') then begin Song.Title := Value; //Add Title Flag to Done Done := Done or 1; end //Artist else if (Identifier = 'ARTIST') then begin Song.Artist := Value; //Add Artist Flag to Done Done := Done or 2; end //MP3 File //Test if Exists else if (Identifier = 'MP3') AND (FileExists(Song.Path + Value)) then begin Song.Mp3 := Value; //Add Mp3 Flag to Done Done := Done or 4; end //Beats per Minute else if (Identifier = 'BPM') then begin // Replace . with , if (Pos('.', Value) <> 0) then Value[Pos('.', Value)] := ','; SetLength(Song.BPM, 1); Song.BPM[0].StartBeat := 0; Song.BPM[0].BPM := StrtoFloatDef(Value, 0) * Mult * MultBPM; if Song.BPM[0].BPM <> 0 then begin //Add BPM Flag to Done Done := Done or 8; end; end //--------- //Additional Header Information //--------- // Video Gap else if (Identifier = 'GAP') then begin // Replace . with , if (Pos('.', Value) <> 0) then Value[Pos('.', Value)] := ','; Song.GAP := StrtoFloatDef (Value, 0); end //Cover Picture else if (Identifier = 'COVER') then begin Song.Cover := Value; end //Background Picture else if (Identifier = 'BACKGROUND') then begin Song.Background := Value; end // Video File else if (Identifier = 'VIDEO') then begin if (FileExists(Song.Path + Value)) then Song.Video := Value else Log.LogError('Can''t find Video File in Song: ' + Song.Path + Song.FileName); end // Video Gap else if (Identifier = 'VIDEOGAP') then begin // Replace . with , if (Pos('.', Value) <> 0) then Value[Pos('.', Value)] := ','; Song.VideoGAP := StrtoFloatDef (Value, 0); end //Genre Sorting else if (Identifier = 'GENRE') then begin Song.Genre := Value; end //Edition Sorting else if (Identifier = 'EDITION') then begin Song.Edition := Value; end //Creator Tag else if (Identifier = 'CREATOR') then begin Song.Creator := Value; end //Language Sorting else if (Identifier = 'LANGUAGE') then begin Song.Language := Value; end // Song Start else if (Identifier = 'START') then begin // Replace . with , if (Pos('.', Value) <> 0) then Value[Pos('.', Value)] := ','; Song.Start := StrtoFloatDef(Value, 0); end // Song Ending else if (Identifier = 'END') then begin TryStrtoInt(Value, Song.Finish); end // Resolution else if (Identifier = 'RESOLUTION') then begin TryStrtoInt(Value, Song.Resolution); end // Notes Gap else if (Identifier = 'NOTESGAP') then begin TryStrtoInt(Value, Song.NotesGAP); end // Relative Notes else if (Identifier = 'RELATIVE') AND (uppercase(Value) = 'YES') then begin Song.Relative := True; end; end; end; if not EOf(SongFile) then ReadLn (SongFile, Line) else begin Result := False; Log.LogError('File Incomplete or not Ultrastar TxT (A): ' + Song.FileName); break; end; {//End on first empty Line if (Length(Line) = 0) then break;} end; //Check if all Required Values are given if (Done <> 15) then begin Result := False; if (Done and 8) = 0 then //No BPM Flag Log.LogError('BPM Tag Missing: ' + Song.FileName) else if (Done and 4) = 0 then //No MP3 Flag Log.LogError('MP3 Tag/File Missing: ' + Song.FileName) else if (Done and 2) = 0 then //No Artist Flag Log.LogError('Artist Tag Missing: ' + Song.FileName) else if (Done and 1) = 0 then //No Title Flag Log.LogError('Title Tag Missing: ' + Song.FileName) else //unknown Error Log.LogError('File Incomplete or not Ultrastar TxT (B - '+ inttostr(Done) +'): ' + Song.FileName); end; end; //-------------------- // Analyse Song File and Read Header //-------------------- function AnalyseFile(var Song: TSong): boolean; begin Result := False; {try } //Reset LineNo FileLineNo := 0; //Open File and set File Pointer to the beginning AssignFile(SongFile, Song.Path + Song.FileName); // if assinged( SongFile ) then begin try Reset(SongFile); //Clear old Song Header ClearSong(Song); //Read Header Result := ReadTxTHeader(Song); //And Close File finally CloseFile(SongFile); end; end; {except CloseFile(SongFile); Result := False; //Error Reporting Log.LogError('An Error occured reading Line ' + inttostr(FileLineNo) + ' from SongHeader: ' + Song.FileName); end;} end; *) //-------------------- // Resets the temporary Sentence Arrays for each Player and some other Variables //-------------------- procedure ResetSingTemp; var Pet: integer; begin SetLength(Czesci, Length(Player)); for Pet := 0 to High(Player) do begin SetLength(Czesci[Pet].Czesc, 1); SetLength(Czesci[Pet].Czesc[0].Nuta, 0); Czesci[Pet].Czesc[0].Lyric := ''; Czesci[Pet].Czesc[0].LyricWidth := 0; Player[pet].Score := 0; Player[pet].IlNut := 0; Player[pet].HighNut := -1; end; //Reset Path and Filename Values to Prevent Errors in Editor if assigned( CurrentSong ) then begin SetLength(CurrentSong.BPM, 0); CurrentSong.Path := ''; CurrentSong.FileName := ''; end; // CurrentSong := nil; end; //-------------------- // Saves a Song //-------------------- function SaveSong(Song: TSong; Czesc: TCzesci; Name: string; Relative: boolean): boolean; var C: integer; N: integer; S: string; B: integer; RelativeSubTime: integer; NoteState: String; begin // Relative := true; // override (idea - use shift+S to save with relative) AssignFile(SongFile, Name); Rewrite(SongFile); WriteLn(SongFile, '#TITLE:' + Song.Title + ''); WriteLn(SongFile, '#ARTIST:' + Song.Artist); if Song.Creator <> '' then WriteLn(SongFile, '#CREATOR:' + Song.Creator); if Song.Edition <> 'Unknown' then WriteLn(SongFile, '#EDITION:' + Song.Edition); if Song.Genre <> 'Unknown' then WriteLn(SongFile, '#GENRE:' + Song.Genre); if Song.Language <> 'Unknown' then WriteLn(SongFile, '#LANGUAGE:' + Song.Language); WriteLn(SongFile, '#MP3:' + Song.Mp3); if Song.Cover <> '' then WriteLn(SongFile, '#COVER:' + Song.Cover); if Song.Background <> '' then WriteLn(SongFile, '#BACKGROUND:' + Song.Background); if Song.Video <> '' then WriteLn(SongFile, '#VIDEO:' + Song.Video); if Song.VideoGAP <> 0 then WriteLn(SongFile, '#VIDEOGAP:' + FloatToStr(Song.VideoGAP)); if Song.Resolution <> 4 then WriteLn(SongFile, '#RESOLUTION:' + IntToStr(Song.Resolution)); if Song.NotesGAP <> 0 then WriteLn(SongFile, '#NOTESGAP:' + IntToStr(Song.NotesGAP)); if Song.Start <> 0 then WriteLn(SongFile, '#START:' + FloatToStr(Song.Start)); if Song.Finish <> 0 then WriteLn(SongFile, '#END:' + IntToStr(Song.Finish)); if Relative then WriteLn(SongFile, '#RELATIVE:yes'); WriteLn(SongFile, '#BPM:' + FloatToStr(Song.BPM[0].BPM / 4)); WriteLn(SongFile, '#GAP:' + FloatToStr(Song.GAP)); RelativeSubTime := 0; for B := 1 to High(CurrentSong.BPM) do WriteLn(SongFile, 'B ' + FloatToStr(CurrentSong.BPM[B].StartBeat) + ' ' + FloatToStr(CurrentSong.BPM[B].BPM/4)); for C := 0 to Czesc.High do begin for N := 0 to Czesc.Czesc[C].HighNut do begin with Czesc.Czesc[C].Nuta[N] do begin //Golden + Freestyle Note Patch case Czesc.Czesc[C].Nuta[N].Wartosc of 0: NoteState := 'F '; 1: NoteState := ': '; 2: NoteState := '* '; end; // case S := NoteState + IntToStr(Start-RelativeSubTime) + ' ' + IntToStr(Dlugosc) + ' ' + IntToStr(Ton) + ' ' + Tekst; WriteLn(SongFile, S); end; // with end; // N if C < Czesc.High then begin // don't write end of last sentence if not Relative then S := '- ' + IntToStr(Czesc.Czesc[C+1].Start) else begin S := '- ' + IntToStr(Czesc.Czesc[C+1].Start - RelativeSubTime) + ' ' + IntToStr(Czesc.Czesc[C+1].Start - RelativeSubTime); RelativeSubTime := Czesc.Czesc[C+1].Start; end; WriteLn(SongFile, S); end; end; // C WriteLn(SongFile, 'E'); CloseFile(SongFile); end; end.