From 015dfecd8b6e4e1463509822ca4179ee88add976 Mon Sep 17 00:00:00 2001 From: jaybinks Date: Thu, 25 Oct 2007 11:33:44 +0000 Subject: first effort to work towards jira#USDX-60 song directory enumeration is now unicode compliant on linux. still needs doing for windows, with wfindfile ( I think thats the right name ) this still relies on sdl_ttf and lots of other changes, but I had a few min and wanted to play with this :) git-svn-id: svn://svn.code.sf.net/p/ultrastardx/svn/trunk@527 b956fd51-792f-4845-bead-9b4dfca2ff2c --- Game/Code/Classes/UFiles.pas | 1528 +++++++++++++++++++++--------------------- 1 file changed, 764 insertions(+), 764 deletions(-) (limited to 'Game/Code/Classes/UFiles.pas') diff --git a/Game/Code/Classes/UFiles.pas b/Game/Code/Classes/UFiles.pas index 9cd3bdc1..7e23b42f 100644 --- a/Game/Code/Classes/UFiles.pas +++ b/Game/Code/Classes/UFiles.pas @@ -1,764 +1,764 @@ -unit UFiles; - -interface - -{$IFDEF FPC} - {$MODE Delphi} -{$ENDIF} - -uses SysUtils, - ULog, - UMusic, - USongs; - -//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 -{* - //Absolute Paths - GamePath: string; - SoundPath: string; - SongPath: string; - LogPath: string; - ThemePath: string; - ScreenshotsPath: string; - CoversPath: string; - LanguagesPath: string; - PluginPath: string; - PlayListPath: string; -*} - - 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, - UMain; - -//-------------------- -// 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)); - - Log.LogError('Identifier: '+Identifier+' - '+ Value ); - - //Check the Identifier (If Value is given) - if (Length(Value) <> 0) then - begin - - //----------- - //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); - Reset(SongFile); - - //Clear old Song Header - ClearSong(Song); - - //Read Header - Result := ReadTxTHeader(Song); - - //And Close File - CloseFile(SongFile); -{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)); - SetLength(AktSong.BPM, 0); - 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 - AktSong.Path := ''; - AktSong.FileName := ''; -end; - -//-------------------- -// Parses Note Infos and save them to Array -//-------------------- -procedure ParseNote(NrCzesci: integer; TypeP: char; StartP, DurationP, NoteP: integer; LyricS: string); -var - Space: boolean; -begin - case Ini.Solmization of - 1: // european - begin - case (NoteP mod 12) of - 0..1: LyricS := ' do '; - 2..3: LyricS := ' re '; - 4: LyricS := ' mi '; - 5..6: LyricS := ' fa '; - 7..8: LyricS := ' sol '; - 9..10: LyricS := ' la '; - 11: LyricS := ' si '; - end; - end; - 2: // japanese - begin - case (NoteP mod 12) of - 0..1: LyricS := ' do '; - 2..3: LyricS := ' re '; - 4: LyricS := ' mi '; - 5..6: LyricS := ' fa '; - 7..8: LyricS := ' so '; - 9..10: LyricS := ' la '; - 11: LyricS := ' shi '; - end; - end; - 3: // american - begin - case (NoteP mod 12) of - 0..1: LyricS := ' do '; - 2..3: LyricS := ' re '; - 4: LyricS := ' mi '; - 5..6: LyricS := ' fa '; - 7..8: LyricS := ' sol '; - 9..10: LyricS := ' la '; - 11: LyricS := ' ti '; - end; - end; - end; // case - - with Czesci[NrCzesci].Czesc[Czesci[NrCzesci].High] do begin - SetLength(Nuta, Length(Nuta) + 1); - IlNut := IlNut + 1; - HighNut := HighNut + 1; - Muzyka.IlNut := Muzyka.IlNut + 1; - - Nuta[HighNut].Start := StartP; - if IlNut = 1 then begin - StartNote := Nuta[HighNut].Start; - if Czesci[NrCzesci].Ilosc = 1 then - Start := -100; -// Start := Nuta[HighNut].Start; - end; - - Nuta[HighNut].Dlugosc := DurationP; - Muzyka.DlugoscNut := Muzyka.DlugoscNut + Nuta[HighNut].Dlugosc; - - // back to the normal system with normal, golden and now freestyle notes - case TypeP of - 'F': Nuta[HighNut].Wartosc := 0; - ':': Nuta[HighNut].Wartosc := 1; - '*': Nuta[HighNut].Wartosc := 2; - end; - - Czesci[NrCzesci].Wartosc := Czesci[NrCzesci].Wartosc + Nuta[HighNut].Dlugosc * Nuta[HighNut].Wartosc; - - Nuta[HighNut].Ton := NoteP; - if Nuta[HighNut].Ton < Base[NrCzesci] then Base[NrCzesci] := Nuta[HighNut].Ton; - Nuta[HighNut].TonGamy := Nuta[HighNut].TonGamy mod 12; - - Nuta[HighNut].Tekst := Copy(LyricS, 2, 100); - Lyric := Lyric + Nuta[HighNut].Tekst; - - if TypeP = 'F' then - Nuta[HighNut].FreeStyle := true; - - Koniec := Nuta[HighNut].Start + Nuta[HighNut].Dlugosc; - end; // with -end; - -//-------------------- -// Called when a new Sentence is found in the TXT File -//-------------------- -procedure NewSentence(NrCzesciP: integer; Param1, Param2: integer); -var -I: Integer; -begin - - // stara czesc //Alter Satz //Update Old Part - Czesci[NrCzesciP].Czesc[Czesci[NrCzesciP].High].BaseNote := Base[NrCzesciP]; - Czesci[NrCzesciP].Czesc[Czesci[NrCzesciP].High].LyricWidth := glTextWidth(PChar(Czesci[NrCzesciP].Czesc[Czesci[NrCzesciP].High].Lyric)); - - //Total Notes Patch - Czesci[NrCzesciP].Czesc[Czesci[NrCzesciP].High].TotalNotes := 0; - for I := low(Czesci[NrCzesciP].Czesc[Czesci[NrCzesciP].High].Nuta) to high(Czesci[NrCzesciP].Czesc[Czesci[NrCzesciP].High].Nuta) do - begin - Czesci[NrCzesciP].Czesc[Czesci[NrCzesciP].High].TotalNotes := Czesci[NrCzesciP].Czesc[Czesci[NrCzesciP].High].TotalNotes + Czesci[NrCzesciP].Czesc[Czesci[NrCzesciP].High].Nuta[I].Dlugosc * Czesci[NrCzesciP].Czesc[Czesci[NrCzesciP].High].Nuta[I].Wartosc; - end; - //Total Notes Patch End - - - // nowa czesc //Neuer Satz //Update New Part - SetLength(Czesci[NrCzesciP].Czesc, Czesci[NrCzesciP].Ilosc + 1); - Czesci[NrCzesciP].High := Czesci[NrCzesciP].High + 1; - Czesci[NrCzesciP].Ilosc := Czesci[NrCzesciP].Ilosc + 1; - Czesci[NrCzesciP].Czesc[Czesci[NrCzesciP].High].HighNut := -1; - - if not AktSong.Relative then - Czesci[NrCzesciP].Czesc[Czesci[NrCzesciP].High].Start := Param1; - - if AktSong.Relative then begin - Czesci[NrCzesciP].Czesc[Czesci[NrCzesciP].High].Start := Param1; - Rel[NrCzesciP] := Rel[NrCzesciP] + Param2; - end; - - Base[NrCzesciP] := 100; // high number -end; - -//-------------------- -// Load a Song -//-------------------- -function LoadSong(Name: string): boolean; -var - TempC: char; - Tekst: string; - CP: integer; // Current Player (0 or 1) - Pet: integer; - Both: boolean; - Param1: integer; - Param2: integer; - Param3: integer; - ParamS: string; - I: Integer; -begin - Result := false; - - if not FileExists(Name) then begin - Log.LogError('File not found: "' + Name + '"', 'WczytajCzesci'); - exit; - end; - - try - MultBPM := 4; // 4 - mnoznik dla czasu nut - Mult := 1; // 4 - dokladnosc pomiaru nut - Base[0] := 100; // high number -// Base[1] := 100; // high number - Czesci[0].Wartosc := 0; -// Czesci[1].Wartosc := 0; // here was the error in 0.3.2 - AktSong.Relative := false; - - Rel[0] := 0; -// Rel[1] := 0; - CP := 0; - Both := false; - if Length(Player) = 2 then Both := true; - - FileMode := fmOpenRead; - AssignFile(SongFile, Name); - Reset(SongFile); - - //Clear old Song Header - ClearSong(AktSong); - - if (AktSong.Path = '') then - AktSong.Path := ExtractFilePath(Name); - - if (AktSong.FileName = '') then - AktSong.Filename := ExtractFileName(Name); - //Read Header - Result := ReadTxTHeader(AktSong); - if not Result then - begin - CloseFile(SongFile); - Log.LogError('Error Loading SongHeader, abort Song Loading'); - Exit; - end; - - Result := False; - - Reset(SongFile); - FileLineNo := 0; - //Search for Note Begining - repeat - ReadLn(SongFile, Tekst); - Inc(FileLineNo); - - if (EoF(SongFile)) then - begin //Song File Corrupted - No Notes - CloseFile(SongFile); - Log.LogError('Could not load txt File, no Notes found: ' + Name); - Result := False; - Exit; - end; - Read(SongFile, TempC); - until ((TempC = ':') or (TempC = 'F') or (TempC = '*')); - - SetLength(Czesci, 2); - for Pet := 0 to High(Czesci) do begin - SetLength(Czesci[Pet].Czesc, 1); - Czesci[Pet].High := 0; - Czesci[Pet].Ilosc := 1; - Czesci[Pet].Akt := 0; - Czesci[Pet].Resolution := AktSong.Resolution; - Czesci[Pet].NotesGAP := AktSong.NotesGAP; - Czesci[Pet].Czesc[0].IlNut := 0; - Czesci[Pet].Czesc[0].HighNut := -1; - end; - -// TempC := ':'; -// TempC := Tekst[1]; // read from backup variable, don't use default ':' value - - while (TempC <> 'E') AND (not EOF(SongFile)) do begin - if (TempC = ':') or (TempC = '*') or (TempC = 'F') then begin - // wczytuje nute - Read(SongFile, Param1); - Read(SongFile, Param2); - Read(SongFile, Param3); - Read(SongFile, ParamS); - - // dodaje nute - if not Both then - // P1 - ParseNote(0, TempC, (Param1+Rel[0]) * Mult, Param2 * Mult, Param3, ParamS) - else begin - // P1 + P2 - ParseNote(0, TempC, (Param1+Rel[0]) * Mult, Param2 * Mult, Param3, ParamS); - ParseNote(1, TempC, (Param1+Rel[1]) * Mult, Param2 * Mult, Param3, ParamS); - end; - end; // if - if TempC = '-' then begin - // reads sentence - Read(SongFile, Param1); - if AktSong.Relative then Read(SongFile, Param2); // read one more data for relative system - - // new sentence - if not Both then - // P1 - NewSentence(0, (Param1 + Rel[0]) * Mult, Param2) - else begin - // P1 + P2 - NewSentence(0, (Param1 + Rel[0]) * Mult, Param2); - NewSentence(1, (Param1 + Rel[1]) * Mult, Param2); - end; - - end; // if - - if TempC = 'B' then begin - SetLength(AktSong.BPM, Length(AktSong.BPM) + 1); - Read(SongFile, AktSong.BPM[High(AktSong.BPM)].StartBeat); - AktSong.BPM[High(AktSong.BPM)].StartBeat := AktSong.BPM[High(AktSong.BPM)].StartBeat + Rel[0]; - - Read(SongFile, Tekst); - AktSong.BPM[High(AktSong.BPM)].BPM := StrToFloat(Tekst); - AktSong.BPM[High(AktSong.BPM)].BPM := AktSong.BPM[High(AktSong.BPM)].BPM * Mult * MultBPM; - end; - - - if not Both then begin - Czesci[CP].Czesc[Czesci[CP].High].BaseNote := Base[CP]; - Czesci[CP].Czesc[Czesci[CP].High].LyricWidth := glTextWidth(PChar(Czesci[CP].Czesc[Czesci[CP].High].Lyric)); - //Total Notes Patch - Czesci[CP].Czesc[Czesci[CP].High].TotalNotes := 0; - for I := low(Czesci[CP].Czesc[Czesci[CP].High].Nuta) to high(Czesci[CP].Czesc[Czesci[CP].High].Nuta) do - begin - Czesci[CP].Czesc[Czesci[CP].High].TotalNotes := Czesci[CP].Czesc[Czesci[CP].High].TotalNotes + Czesci[CP].Czesc[Czesci[CP].High].Nuta[I].Dlugosc * Czesci[CP].Czesc[Czesci[CP].High].Nuta[I].Wartosc; - end; - //Total Notes Patch End - end else begin - for Pet := 0 to High(Czesci) do begin - Czesci[Pet].Czesc[Czesci[Pet].High].BaseNote := Base[Pet]; - Czesci[Pet].Czesc[Czesci[Pet].High].LyricWidth := glTextWidth(PChar(Czesci[Pet].Czesc[Czesci[Pet].High].Lyric)); - //Total Notes Patch - Czesci[Pet].Czesc[Czesci[Pet].High].TotalNotes := 0; - for I := low(Czesci[Pet].Czesc[Czesci[Pet].High].Nuta) to high(Czesci[Pet].Czesc[Czesci[Pet].High].Nuta) do - begin - Czesci[Pet].Czesc[Czesci[Pet].High].TotalNotes := Czesci[Pet].Czesc[Czesci[Pet].High].TotalNotes + Czesci[Pet].Czesc[Czesci[Pet].High].Nuta[I].Dlugosc * Czesci[Pet].Czesc[Czesci[Pet].High].Nuta[I].Wartosc; - end; - //Total Notes Patch End - end; - end; - - Read(SongFile, TempC); - Inc(FileLineNo); - end; // while} - - CloseFile(SongFile); - except - try - CloseFile(SongFile); - except - - end; - - Log.LogError('Error Loading File: "' + Name + '" in Line ' + inttostr(FileLineNo)); - exit; - end; - - Result := true; -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(AktSong.BPM) do - WriteLn(SongFile, 'B ' + FloatToStr(AktSong.BPM[B].StartBeat) + ' ' + FloatToStr(AktSong.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. +unit UFiles; + +interface + +{$IFDEF FPC} + {$MODE Delphi} +{$ENDIF} + +uses SysUtils, + ULog, + UMusic, + USongs; + +//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 +{* + //Absolute Paths + GamePath: string; + SoundPath: string; + SongPath: string; + LogPath: string; + ThemePath: string; + ScreenshotsPath: string; + CoversPath: string; + LanguagesPath: string; + PluginPath: string; + PlayListPath: string; +*} + + 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, + UMain; + +//-------------------- +// 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)); + + Log.LogError('Identifier: '+Identifier+' - '+ Value ); + + //Check the Identifier (If Value is given) + if (Length(Value) <> 0) then + begin + + //----------- + //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); + Reset(SongFile); + + //Clear old Song Header + ClearSong(Song); + + //Read Header + Result := ReadTxTHeader(Song); + + //And Close File + CloseFile(SongFile); +{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)); + SetLength(AktSong.BPM, 0); + 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 + AktSong.Path := ''; + AktSong.FileName := ''; +end; + +//-------------------- +// Parses Note Infos and save them to Array +//-------------------- +procedure ParseNote(NrCzesci: integer; TypeP: char; StartP, DurationP, NoteP: integer; LyricS: string); +var + Space: boolean; +begin + case Ini.Solmization of + 1: // european + begin + case (NoteP mod 12) of + 0..1: LyricS := ' do '; + 2..3: LyricS := ' re '; + 4: LyricS := ' mi '; + 5..6: LyricS := ' fa '; + 7..8: LyricS := ' sol '; + 9..10: LyricS := ' la '; + 11: LyricS := ' si '; + end; + end; + 2: // japanese + begin + case (NoteP mod 12) of + 0..1: LyricS := ' do '; + 2..3: LyricS := ' re '; + 4: LyricS := ' mi '; + 5..6: LyricS := ' fa '; + 7..8: LyricS := ' so '; + 9..10: LyricS := ' la '; + 11: LyricS := ' shi '; + end; + end; + 3: // american + begin + case (NoteP mod 12) of + 0..1: LyricS := ' do '; + 2..3: LyricS := ' re '; + 4: LyricS := ' mi '; + 5..6: LyricS := ' fa '; + 7..8: LyricS := ' sol '; + 9..10: LyricS := ' la '; + 11: LyricS := ' ti '; + end; + end; + end; // case + + with Czesci[NrCzesci].Czesc[Czesci[NrCzesci].High] do begin + SetLength(Nuta, Length(Nuta) + 1); + IlNut := IlNut + 1; + HighNut := HighNut + 1; + Muzyka.IlNut := Muzyka.IlNut + 1; + + Nuta[HighNut].Start := StartP; + if IlNut = 1 then begin + StartNote := Nuta[HighNut].Start; + if Czesci[NrCzesci].Ilosc = 1 then + Start := -100; +// Start := Nuta[HighNut].Start; + end; + + Nuta[HighNut].Dlugosc := DurationP; + Muzyka.DlugoscNut := Muzyka.DlugoscNut + Nuta[HighNut].Dlugosc; + + // back to the normal system with normal, golden and now freestyle notes + case TypeP of + 'F': Nuta[HighNut].Wartosc := 0; + ':': Nuta[HighNut].Wartosc := 1; + '*': Nuta[HighNut].Wartosc := 2; + end; + + Czesci[NrCzesci].Wartosc := Czesci[NrCzesci].Wartosc + Nuta[HighNut].Dlugosc * Nuta[HighNut].Wartosc; + + Nuta[HighNut].Ton := NoteP; + if Nuta[HighNut].Ton < Base[NrCzesci] then Base[NrCzesci] := Nuta[HighNut].Ton; + Nuta[HighNut].TonGamy := Nuta[HighNut].TonGamy mod 12; + + Nuta[HighNut].Tekst := Copy(LyricS, 2, 100); + Lyric := Lyric + Nuta[HighNut].Tekst; + + if TypeP = 'F' then + Nuta[HighNut].FreeStyle := true; + + Koniec := Nuta[HighNut].Start + Nuta[HighNut].Dlugosc; + end; // with +end; + +//-------------------- +// Called when a new Sentence is found in the TXT File +//-------------------- +procedure NewSentence(NrCzesciP: integer; Param1, Param2: integer); +var +I: Integer; +begin + + // stara czesc //Alter Satz //Update Old Part + Czesci[NrCzesciP].Czesc[Czesci[NrCzesciP].High].BaseNote := Base[NrCzesciP]; + Czesci[NrCzesciP].Czesc[Czesci[NrCzesciP].High].LyricWidth := glTextWidth(PChar(Czesci[NrCzesciP].Czesc[Czesci[NrCzesciP].High].Lyric)); + + //Total Notes Patch + Czesci[NrCzesciP].Czesc[Czesci[NrCzesciP].High].TotalNotes := 0; + for I := low(Czesci[NrCzesciP].Czesc[Czesci[NrCzesciP].High].Nuta) to high(Czesci[NrCzesciP].Czesc[Czesci[NrCzesciP].High].Nuta) do + begin + Czesci[NrCzesciP].Czesc[Czesci[NrCzesciP].High].TotalNotes := Czesci[NrCzesciP].Czesc[Czesci[NrCzesciP].High].TotalNotes + Czesci[NrCzesciP].Czesc[Czesci[NrCzesciP].High].Nuta[I].Dlugosc * Czesci[NrCzesciP].Czesc[Czesci[NrCzesciP].High].Nuta[I].Wartosc; + end; + //Total Notes Patch End + + + // nowa czesc //Neuer Satz //Update New Part + SetLength(Czesci[NrCzesciP].Czesc, Czesci[NrCzesciP].Ilosc + 1); + Czesci[NrCzesciP].High := Czesci[NrCzesciP].High + 1; + Czesci[NrCzesciP].Ilosc := Czesci[NrCzesciP].Ilosc + 1; + Czesci[NrCzesciP].Czesc[Czesci[NrCzesciP].High].HighNut := -1; + + if not AktSong.Relative then + Czesci[NrCzesciP].Czesc[Czesci[NrCzesciP].High].Start := Param1; + + if AktSong.Relative then begin + Czesci[NrCzesciP].Czesc[Czesci[NrCzesciP].High].Start := Param1; + Rel[NrCzesciP] := Rel[NrCzesciP] + Param2; + end; + + Base[NrCzesciP] := 100; // high number +end; + +//-------------------- +// Load a Song +//-------------------- +function LoadSong(Name: string): boolean; +var + TempC: char; + Tekst: string; + CP: integer; // Current Player (0 or 1) + Pet: integer; + Both: boolean; + Param1: integer; + Param2: integer; + Param3: integer; + ParamS: string; + I: Integer; +begin + Result := false; + + if not FileExists(Name) then begin + Log.LogError('File not found: "' + Name + '"', 'WczytajCzesci'); + exit; + end; + + try + MultBPM := 4; // 4 - mnoznik dla czasu nut + Mult := 1; // 4 - dokladnosc pomiaru nut + Base[0] := 100; // high number +// Base[1] := 100; // high number + Czesci[0].Wartosc := 0; +// Czesci[1].Wartosc := 0; // here was the error in 0.3.2 + AktSong.Relative := false; + + Rel[0] := 0; +// Rel[1] := 0; + CP := 0; + Both := false; + if Length(Player) = 2 then Both := true; + + FileMode := fmOpenRead; + AssignFile(SongFile, Name); + Reset(SongFile); + + //Clear old Song Header + ClearSong(AktSong); + + if (AktSong.Path = '') then + AktSong.Path := ExtractFilePath(Name); + + if (AktSong.FileName = '') then + AktSong.Filename := ExtractFileName(Name); + //Read Header + Result := ReadTxTHeader(AktSong); + if not Result then + begin + CloseFile(SongFile); + Log.LogError('Error Loading SongHeader, abort Song Loading'); + Exit; + end; + + Result := False; + + Reset(SongFile); + FileLineNo := 0; + //Search for Note Begining + repeat + ReadLn(SongFile, Tekst); + Inc(FileLineNo); + + if (EoF(SongFile)) then + begin //Song File Corrupted - No Notes + CloseFile(SongFile); + Log.LogError('Could not load txt File, no Notes found: ' + Name); + Result := False; + Exit; + end; + Read(SongFile, TempC); + until ((TempC = ':') or (TempC = 'F') or (TempC = '*')); + + SetLength(Czesci, 2); + for Pet := 0 to High(Czesci) do begin + SetLength(Czesci[Pet].Czesc, 1); + Czesci[Pet].High := 0; + Czesci[Pet].Ilosc := 1; + Czesci[Pet].Akt := 0; + Czesci[Pet].Resolution := AktSong.Resolution; + Czesci[Pet].NotesGAP := AktSong.NotesGAP; + Czesci[Pet].Czesc[0].IlNut := 0; + Czesci[Pet].Czesc[0].HighNut := -1; + end; + +// TempC := ':'; +// TempC := Tekst[1]; // read from backup variable, don't use default ':' value + + while (TempC <> 'E') AND (not EOF(SongFile)) do begin + if (TempC = ':') or (TempC = '*') or (TempC = 'F') then begin + // wczytuje nute + Read(SongFile, Param1); + Read(SongFile, Param2); + Read(SongFile, Param3); + Read(SongFile, ParamS); + + // dodaje nute + if not Both then + // P1 + ParseNote(0, TempC, (Param1+Rel[0]) * Mult, Param2 * Mult, Param3, ParamS) + else begin + // P1 + P2 + ParseNote(0, TempC, (Param1+Rel[0]) * Mult, Param2 * Mult, Param3, ParamS); + ParseNote(1, TempC, (Param1+Rel[1]) * Mult, Param2 * Mult, Param3, ParamS); + end; + end; // if + if TempC = '-' then begin + // reads sentence + Read(SongFile, Param1); + if AktSong.Relative then Read(SongFile, Param2); // read one more data for relative system + + // new sentence + if not Both then + // P1 + NewSentence(0, (Param1 + Rel[0]) * Mult, Param2) + else begin + // P1 + P2 + NewSentence(0, (Param1 + Rel[0]) * Mult, Param2); + NewSentence(1, (Param1 + Rel[1]) * Mult, Param2); + end; + + end; // if + + if TempC = 'B' then begin + SetLength(AktSong.BPM, Length(AktSong.BPM) + 1); + Read(SongFile, AktSong.BPM[High(AktSong.BPM)].StartBeat); + AktSong.BPM[High(AktSong.BPM)].StartBeat := AktSong.BPM[High(AktSong.BPM)].StartBeat + Rel[0]; + + Read(SongFile, Tekst); + AktSong.BPM[High(AktSong.BPM)].BPM := StrToFloat(Tekst); + AktSong.BPM[High(AktSong.BPM)].BPM := AktSong.BPM[High(AktSong.BPM)].BPM * Mult * MultBPM; + end; + + + if not Both then begin + Czesci[CP].Czesc[Czesci[CP].High].BaseNote := Base[CP]; + Czesci[CP].Czesc[Czesci[CP].High].LyricWidth := glTextWidth(PChar(Czesci[CP].Czesc[Czesci[CP].High].Lyric)); + //Total Notes Patch + Czesci[CP].Czesc[Czesci[CP].High].TotalNotes := 0; + for I := low(Czesci[CP].Czesc[Czesci[CP].High].Nuta) to high(Czesci[CP].Czesc[Czesci[CP].High].Nuta) do + begin + Czesci[CP].Czesc[Czesci[CP].High].TotalNotes := Czesci[CP].Czesc[Czesci[CP].High].TotalNotes + Czesci[CP].Czesc[Czesci[CP].High].Nuta[I].Dlugosc * Czesci[CP].Czesc[Czesci[CP].High].Nuta[I].Wartosc; + end; + //Total Notes Patch End + end else begin + for Pet := 0 to High(Czesci) do begin + Czesci[Pet].Czesc[Czesci[Pet].High].BaseNote := Base[Pet]; + Czesci[Pet].Czesc[Czesci[Pet].High].LyricWidth := glTextWidth(PChar(Czesci[Pet].Czesc[Czesci[Pet].High].Lyric)); + //Total Notes Patch + Czesci[Pet].Czesc[Czesci[Pet].High].TotalNotes := 0; + for I := low(Czesci[Pet].Czesc[Czesci[Pet].High].Nuta) to high(Czesci[Pet].Czesc[Czesci[Pet].High].Nuta) do + begin + Czesci[Pet].Czesc[Czesci[Pet].High].TotalNotes := Czesci[Pet].Czesc[Czesci[Pet].High].TotalNotes + Czesci[Pet].Czesc[Czesci[Pet].High].Nuta[I].Dlugosc * Czesci[Pet].Czesc[Czesci[Pet].High].Nuta[I].Wartosc; + end; + //Total Notes Patch End + end; + end; + + Read(SongFile, TempC); + Inc(FileLineNo); + end; // while} + + CloseFile(SongFile); + except + try + CloseFile(SongFile); + except + + end; + + Log.LogError('Error Loading File: "' + Name + '" in Line ' + inttostr(FileLineNo)); + exit; + end; + + Result := true; +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(AktSong.BPM) do + WriteLn(SongFile, 'B ' + FloatToStr(AktSong.BPM[B].StartBeat) + ' ' + FloatToStr(AktSong.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. -- cgit v1.2.3