From 2bd6d1d2ea8d36eedac96053a7e4a818da22f654 Mon Sep 17 00:00:00 2001
From: s_alexander <s_alexander@b956fd51-792f-4845-bead-9b4dfca2ff2c>
Date: Thu, 3 Apr 2008 15:00:10 +0000
Subject: rewriting of the txt file parser removed unused variables from TLines
 and TLine, removed TMelody overworked the TLines and TLine records

git-svn-id: svn://svn.code.sf.net/p/ultrastardx/svn/trunk@1000 b956fd51-792f-4845-bead-9b4dfca2ff2c
---
 Game/Code/Classes/UDraw.pas          |   48 +-
 Game/Code/Classes/UFiles.pas         |    6 +-
 Game/Code/Classes/ULyrics.pas        |   47 +-
 Game/Code/Classes/ULyrics_bak.pas    |    2 +-
 Game/Code/Classes/UMain.pas          | 2253 +++++++++++++++++-----------------
 Game/Code/Classes/UMusic.pas         |   21 +-
 Game/Code/Classes/USong.pas          |  365 +++---
 Game/Code/Classes/USong_TextFile.pas |   86 ++
 Game/Code/Classes/USong_Txt.pas      |  438 +++++++
 9 files changed, 1903 insertions(+), 1363 deletions(-)
 create mode 100644 Game/Code/Classes/USong_TextFile.pas
 create mode 100644 Game/Code/Classes/USong_Txt.pas

(limited to 'Game/Code/Classes')

diff --git a/Game/Code/Classes/UDraw.pas b/Game/Code/Classes/UDraw.pas
index 18c347a7..4b7d02fc 100644
--- a/Game/Code/Classes/UDraw.pas
+++ b/Game/Code/Classes/UDraw.pas
@@ -202,16 +202,16 @@ var
   Pet:    integer;
   TempR:  real;
 begin
-  TempR := (Right-Left) / (Lines[NrCzesci].Line[Lines[NrCzesci].Current].End_ - Lines[NrCzesci].Line[Lines[NrCzesci].Current].StartNote);
+  TempR := (Right-Left) / (Lines[NrCzesci].Line[Lines[NrCzesci].Current].End_ - Lines[NrCzesci].Line[Lines[NrCzesci].Current].Note[0].Start);
   glEnable(GL_BLEND);
   glBegin(GL_LINES);
-  for Pet := Lines[NrCzesci].Line[Lines[NrCzesci].Current].StartNote to Lines[NrCzesci].Line[Lines[NrCzesci].Current].End_ do begin
+  for Pet := Lines[NrCzesci].Line[Lines[NrCzesci].Current].Note[0].Start to Lines[NrCzesci].Line[Lines[NrCzesci].Current].End_ do begin
     if (Pet mod Lines[NrCzesci].Resolution) = Lines[NrCzesci].NotesGAP then
       glColor4f(0, 0, 0, 1)
     else
       glColor4f(0, 0, 0, 0.3);
-    glVertex2f(Left + TempR * (Pet - Lines[NrCzesci].Line[Lines[NrCzesci].Current].StartNote), Top);
-    glVertex2f(Left + TempR * (Pet - Lines[NrCzesci].Line[Lines[NrCzesci].Current].StartNote), Top + 135);
+    glVertex2f(Left + TempR * (Pet - Lines[NrCzesci].Line[Lines[NrCzesci].Current].Note[0].Start), Top);
+    glVertex2f(Left + TempR * (Pet - Lines[NrCzesci].Line[Lines[NrCzesci].Current].Note[0].Start), Top + 135);
   end;
   glEnd;
   glDisable(GL_BLEND);
@@ -248,7 +248,7 @@ begin
   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
 
   lTmpA := (Right-Left);
-  lTmpB := (Lines[NrCzesci].Line[Lines[NrCzesci].Current].End_ - Lines[NrCzesci].Line[Lines[NrCzesci].Current].StartNote);
+  lTmpB := (Lines[NrCzesci].Line[Lines[NrCzesci].Current].End_ - Lines[NrCzesci].Line[Lines[NrCzesci].Current].Note[0].Start);
 
   if ( lTmpA > 0 ) AND
      ( lTmpB > 0 ) THEN
@@ -264,22 +264,22 @@ begin
   with Lines[NrCzesci].Line[Lines[NrCzesci].Current] do begin
     for Pet := 0 to HighNote do begin
       with Note[Pet] do begin
-        if not FreeStyle then begin
+        if NoteType <> ntFreestyle then begin
 
 
           if Ini.EffectSing = 0 then
           // If Golden note Effect of then Change not Color
           begin
             case NoteType of
-              1: glColor4f(1, 1, 1, 1);   // We set alpha to 1, cause we can control the transparency through the png itself
-              2: glColor4f(1, 1, 0.3, 1); // no stars, paint yellow -> glColor4f(1, 1, 0.3, 0.85); - we could
+              ntNormal: glColor4f(1, 1, 1, 1);   // We set alpha to 1, cause we can control the transparency through the png itself
+              ntGolden: glColor4f(1, 1, 0.3, 1); // no stars, paint yellow -> glColor4f(1, 1, 0.3, 0.85); - we could
             end; // case
           end //Else all Notes same Color
           else
             glColor4f(1, 1, 1, 1);        // We set alpha to 1, cause we can control the transparency through the png itself
                                           // Czesci == teil, element == piece, element | koniec == end / ending
           // lewa czesc  -  left part
-          Rec.Left := (Start-Lines[NrCzesci].Line[Lines[NrCzesci].Current].StartNote) * TempR + Left + 0.5 + 10*ScreenX;
+          Rec.Left := (Start-Lines[NrCzesci].Line[Lines[NrCzesci].Current].Note[0].Start) * TempR + Left + 0.5 + 10*ScreenX;
           Rec.Right := Rec.Left + NotesW;
           Rec.Top := Top - (Tone-BaseNote)*Space/2 - NotesH;
           Rec.Bottom := Rec.Top + 2 * NotesH;
@@ -297,7 +297,7 @@ begin
 
          // srodkowa czesc  -  middle part
         Rec.Left := Rec.Right;
-        Rec.Right := (Start+Length-Lines[NrCzesci].Line[Lines[NrCzesci].Current].StartNote) * TempR + Left - NotesW - 0.5 + 10*ScreenX;    // Dlugosc == length
+        Rec.Right := (Start+Length-Lines[NrCzesci].Line[Lines[NrCzesci].Current].Note[0].Start) * TempR + Left - NotesW - 0.5 + 10*ScreenX;    // Dlugosc == length
 
         glBindTexture(GL_TEXTURE_2D, Tex_plain_Mid[PlayerNumber].TexNum);
         glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT );
@@ -322,7 +322,7 @@ begin
         glEnd;
 
           // Golden Star Patch
-          if (NoteType = 2) AND (Ini.EffectSing=1) then
+          if (NoteType = ntGolden) AND (Ini.EffectSing=1) then
           begin
             GoldenRec.SaveGoldenStarsRec(GoldenStarPos, Rec.Top, Rec.Right, Rec.Bottom);
           end;
@@ -365,13 +365,13 @@ var
 
 ////  if Player[NrGracza].IlNut > 0 then
     begin
-      TempR := W / (Lines[0].Line[Lines[0].Current].End_ - Lines[0].Line[Lines[0].Current].StartNote);
+      TempR := W / (Lines[0].Line[Lines[0].Current].End_ - Lines[0].Line[Lines[0].Current].Note[0].Start);
         for N := 0 to Player[NrGracza].HighNote do
           begin
             with Player[NrGracza].Note[N] do
               begin
                 // Left part of note
-                Rec.Left := X + (Start-Lines[0].Line[Lines[0].Current].StartNote) * TempR + 0.5 + 10*ScreenX;
+                Rec.Left := X + (Start-Lines[0].Line[Lines[0].Current].Note[0].Start) * TempR + 0.5 + 10*ScreenX;
                 Rec.Right := Rec.Left + NotesW;
 
                 // Draw it in half size, if not hit
@@ -399,7 +399,7 @@ var
 
                // Middle part of the note
                Rec.Left := Rec.Right;
-               Rec.Right := X + (Start+Length-Lines[0].Line[Lines[0].Current].StartNote) * TempR - NotesW - 0.5  + 10*ScreenX;
+               Rec.Right := X + (Start+Length-Lines[0].Line[Lines[0].Current].Note[0].Start) * TempR - NotesW - 0.5  + 10*ScreenX;
 
                // (nowe) - dunno
                if (Start+Length-1 = LineState.CurrentBeatD) then
@@ -480,7 +480,7 @@ begin
 
 
   lTmpA := (Right-Left);
-  lTmpB := (Lines[NrCzesci].Line[Lines[NrCzesci].Current].End_ - Lines[NrCzesci].Line[Lines[NrCzesci].Current].StartNote);
+  lTmpB := (Lines[NrCzesci].Line[Lines[NrCzesci].Current].End_ - Lines[NrCzesci].Line[Lines[NrCzesci].Current].Note[0].Start);
 
 
   if ( lTmpA > 0 ) AND
@@ -496,16 +496,16 @@ begin
   with Lines[NrCzesci].Line[Lines[NrCzesci].Current] do begin
     for Pet := 0 to HighNote do begin
       with Note[Pet] do begin
-        if not FreeStyle then begin
+        if NoteType <> ntFreestyle then begin
           // begin: 14, 20
           // easy: 6, 11
           W := NotesW * 2 + 2;
           H := NotesH * 1.5 + 3.5;
 
-          X2 := (Start-Lines[NrCzesci].Line[Lines[NrCzesci].Current].StartNote) * TempR + Left + 0.5 + 10*ScreenX + 4; // wciecie
+          X2 := (Start-Lines[NrCzesci].Line[Lines[NrCzesci].Current].Note[0].Start) * TempR + Left + 0.5 + 10*ScreenX + 4; // wciecie
           X1 := X2-W;
 
-          X3 := (Start+Length-Lines[NrCzesci].Line[Lines[NrCzesci].Current].StartNote) * TempR + Left - 0.5 + 10*ScreenX - 4; // wciecie
+          X3 := (Start+Length-Lines[NrCzesci].Line[Lines[NrCzesci].Current].Note[0].Start) * TempR + Left - 0.5 + 10*ScreenX - 4; // wciecie
           X4 := X3+W;
 
           // left
@@ -1235,22 +1235,22 @@ begin
   glEnable(GL_TEXTURE_2D);
   glEnable(GL_BLEND);
   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-  TempR := (Right-Left) / (Lines[NrCzesci].Line[Lines[NrCzesci].Current].End_ - Lines[NrCzesci].Line[Lines[NrCzesci].Current].StartNote);
+  TempR := (Right-Left) / (Lines[NrCzesci].Line[Lines[NrCzesci].Current].End_ - Lines[NrCzesci].Line[Lines[NrCzesci].Current].Note[0].Start);
   with Lines[NrCzesci].Line[Lines[NrCzesci].Current] do begin
     for Pet := 0 to HighNote do begin
       with Note[Pet] do begin
 
           // Golden Note Patch
           case NoteType of
-            0: glColor4f(1, 1, 1, 0.35);
-            1: glColor4f(1, 1, 1, 0.85);
-            2: glColor4f(1, 1, 0.3, 0.85);
+            ntFreestyle: glColor4f(1, 1, 1, 0.35);
+            ntNormal: glColor4f(1, 1, 1, 0.85);
+            ntGolden: Glcolor4f(1, 1, 0.3, 0.85);
           end; // case
 
 
 
           // lewa czesc  -  left part
-          Rec.Left := (Start-Lines[NrCzesci].Line[Lines[NrCzesci].Current].StartNote) * TempR + Left + 0.5 + 10*ScreenX;
+          Rec.Left := (Start-Lines[NrCzesci].Line[Lines[NrCzesci].Current].Note[0].Start) * TempR + Left + 0.5 + 10*ScreenX;
           Rec.Right := Rec.Left + NotesW;
           Rec.Top := Top - (Tone-BaseNote)*Space/2 - NotesH;
           Rec.Bottom := Rec.Top + 2 * NotesH;
@@ -1264,7 +1264,7 @@ begin
 
          // srodkowa czesc  -  middle part
         Rec.Left := Rec.Right;
-        Rec.Right := (Start+Length-Lines[NrCzesci].Line[Lines[NrCzesci].Current].StartNote) * TempR + Left - NotesW - 0.5 + 10*ScreenX;
+        Rec.Right := (Start+Length-Lines[NrCzesci].Line[Lines[NrCzesci].Current].Note[0].Start) * TempR + Left - NotesW - 0.5 + 10*ScreenX;
 
         glBindTexture(GL_TEXTURE_2D, Tex_Mid[Color].TexNum);
         glBegin(GL_QUADS);
diff --git a/Game/Code/Classes/UFiles.pas b/Game/Code/Classes/UFiles.pas
index 076db441..6b506574 100644
--- a/Game/Code/Classes/UFiles.pas
+++ b/Game/Code/Classes/UFiles.pas
@@ -116,9 +116,9 @@ begin
 
         //Golden + Freestyle Note Patch
         case Lines.Line[C].Note[N].NoteType of
-          0: NoteState := 'F ';
-          1: NoteState := ': ';
-          2: NoteState := '* ';
+          ntFreestyle: NoteState := 'F ';
+          ntNormal: NoteState := ': ';
+          ntGolden: NoteState := '* ';
         end; // case
         S := NoteState + IntToStr(Start-RelativeSubTime) + ' ' + IntToStr(Length) + ' ' + IntToStr(Tone) + ' ' + Text;
 
diff --git a/Game/Code/Classes/ULyrics.pas b/Game/Code/Classes/ULyrics.pas
index 909ee835..09031cb5 100644
--- a/Game/Code/Classes/ULyrics.pas
+++ b/Game/Code/Classes/ULyrics.pas
@@ -368,9 +368,9 @@ begin
       LyricLine.Words[I].Start     := Line.Note[I].Start;
       LyricLine.Words[I].Length    := Line.Note[I].Length;
       LyricLine.Words[I].Text      := Line.Note[I].Text;
-      LyricLine.Words[I].Freestyle := Line.Note[I].FreeStyle;
+      LyricLine.Words[I].Freestyle := Line.Note[I].NoteType = ntFreestyle;
       
-      LyricLine.HasFreestyle       := LyricLine.HasFreestyle OR Line.Note[I].FreeStyle;
+      LyricLine.HasFreestyle       := LyricLine.HasFreestyle OR LyricLine.Words[I].Freestyle;
       LyricLine.Text               := LyricLine.Text + LyricLine.Words[I].Text;
         
       if (I > 0) AND LyricLine.Words[I-1].Freestyle AND not LyricLine.Words[I].Freestyle then
@@ -556,7 +556,7 @@ begin
   
   // this is actually a bit more than the real font size
   // it helps adjusting the "zoom-center"
-  LyricsHeight:=30 * (Line^.Size/10)+16;
+  LyricsHeight:=30.5 * (Line^.Size/10);
   
   {
   // duet mode
@@ -572,8 +572,8 @@ begin
   LyricX2 := LyricX + Line^.Width;
    
   // maybe center smaller lines
-  LyricY := Y;
-  //LyricY := Y + ((Size / Line.Size - 1) * LyricsHeight) / 2; 
+  //LyricY := Y;
+  LyricY := Y + ((Size / Line.Size - 1) * LyricsHeight) / 2; 
   
   Alpha := 1;
   
@@ -641,7 +641,7 @@ begin
           end;
       end;
     end;
-    
+        
     glEnable(GL_BLEND);
     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
     glEnable(GL_TEXTURE_2D);
@@ -661,6 +661,15 @@ begin
       glTexCoord2f((CurWordStart+FreestyleDiff)/1024, 1); glVertex2f(LyricX+CurWordStart+FreestyleDiff, LyricY);
     glEnd;
 
+    // draw rest of sentence
+    glColorRGB(LineColor_en);
+    glBegin(GL_QUADS);
+      glTexCoord2f((CurWordEnd+FreestyleDiff)/1024, 1); glVertex2f(LyricX+CurWordEnd+FreestyleDiff, LyricY);
+      glTexCoord2f(CurWordEnd/1024, 1-LyricsHeight/64); glVertex2f(LyricX+CurWordEnd, LyricY + LyricsHeight);
+      glTexCoord2f(Line^.Width/1024, 1-LyricsHeight/64); glVertex2f(LyricX2, LyricY + LyricsHeight);
+      glTexCoord2f(Line^.Width/1024, 1); glVertex2f(LyricX2, LyricY);
+    glEnd;
+    
     // draw active word:
     // type 0: simple lyric effect
     // type 3: ball lyric effect
@@ -703,31 +712,21 @@ begin
       glScalef(1.0+(1-progress)/2,1.0+(1-progress)/2,1.0);
       glColor4f(LineColor_en.r,LineColor_en.g,LineColor_en.b,1-progress);
       glBegin(GL_QUADS);
-        glTexCoord2f((CurWordStart+FreestyleDiff)/1024+0.0001, 1); glVertex2f(-(CurWordEnd-CurWordStart)/2+FreestyleDiff, -LyricsHeight/2);
-        glTexCoord2f(CurWordStart/1024+0.0001, 1-LyricsHeight/64); glVertex2f(-(CurWordEnd-CurWordStart)/2, + LyricsHeight/2);
-        glTexCoord2f(CurWordEnd/1024-0.0001, 1-LyricsHeight/64); glVertex2f((CurWordEnd-CurWordStart)/2, + LyricsHeight/2);
-        glTexCoord2f((CurWordEnd+FreestyleDiff)/1024-0.0001, 1); glVertex2f((CurWordEnd-CurWordStart)/2+FreestyleDiff, -LyricsHeight/2);
+        glTexCoord2f((CurWordStart+FreestyleDiff)/1024, 1); glVertex2f(-(CurWordEnd-CurWordStart)/2+FreestyleDiff, -LyricsHeight/2);
+        glTexCoord2f(CurWordStart/1024, 1-LyricsHeight/64); glVertex2f(-(CurWordEnd-CurWordStart)/2, + LyricsHeight/2);
+        glTexCoord2f(CurWordEnd/1024, 1-LyricsHeight/64); glVertex2f((CurWordEnd-CurWordStart)/2, + LyricsHeight/2);
+        glTexCoord2f((CurWordEnd+FreestyleDiff)/1024, 1); glVertex2f((CurWordEnd-CurWordStart)/2+FreestyleDiff, -LyricsHeight/2);
       glEnd;
       glColor4f(LineColor_act.r,LineColor_act.g,LineColor_act.b,1);
       glBegin(GL_QUADS);
-        glTexCoord2f((CurWordStart+FreestyleDiff)/1024+0.0001, 1); glVertex2f(-(CurWordEnd-CurWordStart)/2+FreestyleDiff, -LyricsHeight/2);
-        glTexCoord2f(CurWordStart/1024+0.0001, 1-LyricsHeight/64); glVertex2f(-(CurWordEnd-CurWordStart)/2,  + LyricsHeight/2);
-        glTexCoord2f(CurWordEnd/1024-0.0001, 1-LyricsHeight/64); glVertex2f((CurWordEnd-CurWordStart)/2,  + LyricsHeight/2);
-        glTexCoord2f((CurWordEnd+FreestyleDiff)/1024-0.0001, 1); glVertex2f((CurWordEnd-CurWordStart)/2+FreestyleDiff, -LyricsHeight/2);
+        glTexCoord2f((CurWordStart+FreestyleDiff)/1024, 1); glVertex2f(-(CurWordEnd-CurWordStart)/2+FreestyleDiff, -LyricsHeight/2);
+        glTexCoord2f(CurWordStart/1024, 1-LyricsHeight/64); glVertex2f(-(CurWordEnd-CurWordStart)/2,  + LyricsHeight/2);
+        glTexCoord2f(CurWordEnd/1024, 1-LyricsHeight/64); glVertex2f((CurWordEnd-CurWordStart)/2,  + LyricsHeight/2);
+        glTexCoord2f((CurWordEnd+FreestyleDiff)/1024, 1); glVertex2f((CurWordEnd-CurWordStart)/2+FreestyleDiff, -LyricsHeight/2);
       glEnd;
       glPopMatrix;
     end;
 
-    // draw rest of sentence
-    glColorRGB(LineColor_en);
-    glBegin(GL_QUADS);
-      glTexCoord2f((CurWordEnd+FreestyleDiff)/1024, 1); glVertex2f(LyricX+CurWordEnd+FreestyleDiff, LyricY);
-      glTexCoord2f(CurWordEnd/1024, 1-LyricsHeight/64); glVertex2f(LyricX+CurWordEnd, LyricY + LyricsHeight);
-      glTexCoord2f(Line^.Width/1024, 1-LyricsHeight/64); glVertex2f(LyricX2, LyricY + LyricsHeight);
-      glTexCoord2f(Line^.Width/1024, 1); glVertex2f(LyricX2, LyricY);
-    glEnd;
-
-
     glDisable(GL_TEXTURE_2D);
     glDisable(GL_BLEND);
     
diff --git a/Game/Code/Classes/ULyrics_bak.pas b/Game/Code/Classes/ULyrics_bak.pas
index 620a28ad..e44800d6 100644
--- a/Game/Code/Classes/ULyrics_bak.pas
+++ b/Game/Code/Classes/ULyrics_bak.pas
@@ -265,7 +265,7 @@ var
 begin
   Clear;
   for N := 0 to Lines[0].Line[NrCzesci].HighNote do begin
-    Italic := Lines[0].Line[NrCzesci].Note[N].FreeStyle;
+    Italic := Lines[0].Line[NrCzesci].Note[N].NoteType = ntFreestyle;
     AddWord(Lines[0].Line[NrCzesci].Note[N].Text);
     Text := Text + Lines[0].Line[NrCzesci].Note[N].Text;
   end;
diff --git a/Game/Code/Classes/UMain.pas b/Game/Code/Classes/UMain.pas
index 9459334d..baefeff8 100644
--- a/Game/Code/Classes/UMain.pas
+++ b/Game/Code/Classes/UMain.pas
@@ -1,1131 +1,1122 @@
-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);
-var
-  CP:     integer; // current player
-  S:      integer; // sentence
-  SMin:   integer;
-  SMax:   integer;
-  SDet:   integer; // temporary: sentence of detected note
-  Count:  integer;
-  Mozna:  boolean;
-  New:    boolean;
-  Range:  integer;
-  NoteHit:boolean;
-begin
-  //  Log.LogStatus('Beat ' + IntToStr(LineState.CurrentBeat) + ' HalfBeat ' + IntToStr(LineState.AktHalf), 'NewBeat');
-
-  // On linux we get an AV @ NEWNOTE,  line 600 of Classes/UMain.pas
-  if not assigned( AudioInputProcessor.Sound ) then
-    exit;
-
-  // analizuje dla obu graczy ten sam sygnal (Sound.OneSrcForBoth)
-  // albo juz lepiej nie
-  for CP := 0 to PlayersPlay-1 do
-  begin
-    // analyze buffer
-    AudioInputProcessor.Sound[CP].AnalyzeBuffer;
-
-    // adds some noise
-    //LineState.Tone := LineState.Tone + Round(Random(3)) - 1;
-
-    // count min and max sentence range for checking (detection is delayed to the notes we see on the screen)
-    SMin := Lines[0].Current-1;
-    if SMin < 0 then
-      SMin := 0;
-    SMax := Lines[0].Current;
-
-    // check if we can add new note
-    Mozna := false;
-    SDet:=SMin;
-    for S := SMin to SMax do
-    begin
-      for Count := 0 to Lines[0].Line[S].HighNote do
-      begin
-        if ((Lines[0].Line[S].Note[Count].Start <= LineState.CurrentBeatD)
-          and (Lines[0].Line[S].Note[Count].Start + Lines[0].Line[S].Note[Count].Length - 1 >= LineState.CurrentBeatD))
-          and (not Lines[0].Line[S].Note[Count].FreeStyle) // but don't allow when it's FreeStyle note
-          and (Lines[0].Line[S].Note[Count].Length > 0) then // and make sure the note lengths is at least 1
-        begin
-          SDet := S;
-          Mozna := true;
-          Break;
-        end;
-      end;
-    end;
-
-    S := SDet;
-
-    //Czas.SzczytJest := true;
-    //Czas.Tone := 27;
-
-    // gdy moze, to dodaje nute - When Mozna, it adds note (?)
-    if (AudioInputProcessor.Sound[CP].ToneValid) and (Mozna) then
-    begin
-      // operowanie na ostatniej nucie
-      for Count := 0 to Lines[0].Line[S].HighNote do
-      begin
-        if (Lines[0].Line[S].Note[Count].Start <= LineState.OldBeatD+1) and
-           (Lines[0].Line[S].Note[Count].Start +
-            Lines[0].Line[S].Note[Count].Length > LineState.OldBeatD+1) then
-        begin
-          // to robi, tylko dla pary nut (oryginalnej i gracza)
-
-          // przesuwanie tonu w odpowiednia game
-          while (AudioInputProcessor.Sound[CP].Tone - Lines[0].Line[S].Note[Count].Tone > 6) do
-            AudioInputProcessor.Sound[CP].Tone := AudioInputProcessor.Sound[CP].Tone - 12;
-
-          while (AudioInputProcessor.Sound[CP].Tone - Lines[0].Line[S].Note[Count].Tone < -6) do
-            AudioInputProcessor.Sound[CP].Tone := AudioInputProcessor.Sound[CP].Tone + 12;
-
-          // Half size Notes Patch
-          NoteHit := false;
-
-          //if Ini.Difficulty = 0 then Range := 2;
-          //if Ini.Difficulty = 1 then Range := 1;
-          //if Ini.Difficulty = 2 then Range := 0;
-          Range := 2 - Ini.Difficulty;
-
-          if abs(Lines[0].Line[S].Note[Count].Tone - AudioInputProcessor.Sound[CP].Tone) <= Range then
-          begin
-            AudioInputProcessor.Sound[CP].Tone := Lines[0].Line[S].Note[Count].Tone;
-
-            // Half size Notes Patch
-            NoteHit := true;
-
-            if (Ini.LineBonus = 0) then
-            begin
-              // add points without LineBonus
-              case Lines[0].Line[S].Note[Count].NoteType of
-                1:  Player[CP].Score := Player[CP].Score + 10000 / Lines[0].NoteType *
-                      Lines[0].Line[S].Note[Count].NoteType;
-                2:  Player[CP].ScoreGolden := Player[CP].ScoreGolden + 10000 / Lines[0].NoteType *
-                      Lines[0].Line[S].Note[Count].NoteType;
-              end;
-            end
-            else
-            begin
-              // add points with Line Bonus
-              case Lines[0].Line[S].Note[Count].NoteType of
-                1:  Player[CP].Score := Player[CP].Score + 9000 / Lines[0].NoteType *
-                      Lines[0].Line[S].Note[Count].NoteType;
-                2:  Player[CP].ScoreGolden := Player[CP].ScoreGolden + 9000 / Lines[0].NoteType *
-                      Lines[0].Line[S].Note[Count].NoteType;
-              end;
-            end;
-
-            Player[CP].ScoreI := Floor(Player[CP].Score / 10) * 10;
-            Player[CP].ScoreGoldenI := Floor(Player[CP].ScoreGolden / 10) * 10;
-
-            Player[CP].ScoreTotalI := Player[CP].ScoreI + Player[CP].ScoreGoldenI + Player[CP].ScoreLineI;
-          end;
-
-        end; // operowanie
-      end; // for
-
-      // sprawdzanie czy to nowa nuta, czy przedluzenie
-      if S = SMax then
-      begin
-        New := true;
-        // if last has the same tone
-        if (Player[CP].IlNut > 0 ) and
-           (Player[CP].Note[Player[CP].HighNote].Tone = AudioInputProcessor.Sound[CP].Tone) and
-           (Player[CP].Note[Player[CP].HighNote].Start + Player[CP].Note[Player[CP].HighNote].Length = LineState.CurrentBeatD) then
-        begin
-          New := false;
-        end;
-
-        // if is not as new note to control "beacie" (TODO: translate polish "beacie")
-        for Count := 0 to Lines[0].Line[S].HighNote do
-        begin
-          if (Lines[0].Line[S].Note[Count].Start = LineState.CurrentBeatD) then
-            New := true;
-        end;
-
-        // dodawanie nowej nuty
-        if New then
-        begin
-          // New Note
-          Player[CP].IlNut := Player[CP].IlNut + 1;
-          Player[CP].HighNote := Player[CP].HighNote + 1;
-          SetLength(Player[CP].Note, Player[CP].IlNut);
-          Player[CP].Note[Player[CP].HighNote].Start   := LineState.CurrentBeatD;
-          Player[CP].Note[Player[CP].HighNote].Length := 1;
-          Player[CP].Note[Player[CP].HighNote].Tone     := AudioInputProcessor.Sound[CP].Tone; // Ton || TonDokl
-          Player[CP].Note[Player[CP].HighNote].Detekt  := LineState.MidBeat;
-
-          // Half Note Patch
-          Player[CP].Note[Player[CP].HighNote].Hit := NoteHit;
-
-          //Log.LogStatus('New Note ' + IntToStr(Gracz.Note[Gracz.HighNote].Start), 'NewBeat');
-        end
-        else
-        begin
-          // przedluzenie nuty
-          Player[CP].Note[Player[CP].HighNote].Length := Player[CP].Note[Player[CP].HighNote].Length + 1;
-        end;
-
-        // check for perfect note and then lit the star (on Draw)
-        for Count := 0 to Lines[0].Line[S].HighNote do
-        begin
-          if (Lines[0].Line[S].Note[Count].Start = Player[CP].Note[Player[CP].HighNote].Start) and
-             (Lines[0].Line[S].Note[Count].Length = Player[CP].Note[Player[CP].HighNote].Length) and
-             (Lines[0].Line[S].Note[Count].Tone = Player[CP].Note[Player[CP].HighNote].Tone) then
-          begin
-            Player[CP].Note[Player[CP].HighNote].Perfect := true;
-          end;
-        end;
-      end; // if S = SMax
-
-    end; // if moze
-  end; // for CP
-  //  Log.LogStatus('EndBeat', 'NewBeat');
-
-  //On Sentence End -> For LineBonus + SingBar
-  if (sDet >= low(Lines[0].Line)) and (sDet <= high(Lines[0].Line)) then
-  begin
-    if assigned( Sender ) and
-       ((Lines[0].Line[SDet].Note[Lines[0].Line[SDet].HighNote].Start + Lines[0].Line[SDet].Note[Lines[0].Line[SDet].HighNote].Length - 1) = LineState.CurrentBeatD) then
-    begin
-      Sender.onSentenceEnd(sDet);
-    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.
-
+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);
+var
+  CP:     integer; // current player
+  S:      integer; // sentence
+  SMin:   integer;
+  SMax:   integer;
+  SDet:   integer; // temporary: sentence of detected note
+  Count:  integer;
+  Mozna:  boolean;
+  New:    boolean;
+  Range:  integer;
+  NoteHit:boolean;
+  MaxPoints: integer; // maximal points without line bonus
+begin
+  //  Log.LogStatus('Beat ' + IntToStr(LineState.CurrentBeat) + ' HalfBeat ' + IntToStr(LineState.AktHalf), 'NewBeat');
+
+  // On linux we get an AV @ NEWNOTE,  line 600 of Classes/UMain.pas
+  if not assigned( AudioInputProcessor.Sound ) then
+    exit;
+
+  // analizuje dla obu graczy ten sam sygnal (Sound.OneSrcForBoth)
+  // albo juz lepiej nie
+  for CP := 0 to PlayersPlay-1 do
+  begin
+    // analyze buffer
+    AudioInputProcessor.Sound[CP].AnalyzeBuffer;
+
+    // adds some noise
+    //LineState.Tone := LineState.Tone + Round(Random(3)) - 1;
+
+    // count min and max sentence range for checking (detection is delayed to the notes we see on the screen)
+    SMin := Lines[0].Current-1;
+    if SMin < 0 then
+      SMin := 0;
+    SMax := Lines[0].Current;
+
+    // check if we can add new note
+    Mozna := false;
+    SDet:=SMin;
+    for S := SMin to SMax do
+    begin
+      for Count := 0 to Lines[0].Line[S].HighNote do
+      begin
+        if ((Lines[0].Line[S].Note[Count].Start <= LineState.CurrentBeatD)
+          and (Lines[0].Line[S].Note[Count].Start + Lines[0].Line[S].Note[Count].Length - 1 >= LineState.CurrentBeatD))
+          and (Lines[0].Line[S].Note[Count].NoteType <> ntFreestyle) // but don't allow when it's FreeStyle note
+          and (Lines[0].Line[S].Note[Count].Length > 0) then // and make sure the note lengths is at least 1
+        begin
+          SDet := S;
+          Mozna := true;
+          Break;
+        end;
+      end;
+    end;
+
+    S := SDet;
+
+    //Czas.SzczytJest := true;
+    //Czas.Tone := 27;
+
+    // gdy moze, to dodaje nute - When Mozna, it adds note (?)
+    if (AudioInputProcessor.Sound[CP].ToneValid) and (Mozna) then
+    begin
+      // operowanie na ostatniej nucie
+      for Count := 0 to Lines[0].Line[S].HighNote do
+      begin
+        if (Lines[0].Line[S].Note[Count].Start <= LineState.OldBeatD+1) and
+           (Lines[0].Line[S].Note[Count].Start +
+            Lines[0].Line[S].Note[Count].Length > LineState.OldBeatD+1) then
+        begin
+          // to robi, tylko dla pary nut (oryginalnej i gracza)
+
+          // przesuwanie tonu w odpowiednia game
+          while (AudioInputProcessor.Sound[CP].Tone - Lines[0].Line[S].Note[Count].Tone > 6) do
+            AudioInputProcessor.Sound[CP].Tone := AudioInputProcessor.Sound[CP].Tone - 12;
+
+          while (AudioInputProcessor.Sound[CP].Tone - Lines[0].Line[S].Note[Count].Tone < -6) do
+            AudioInputProcessor.Sound[CP].Tone := AudioInputProcessor.Sound[CP].Tone + 12;
+
+          // Half size Notes Patch
+          NoteHit := false;
+
+          //if Ini.Difficulty = 0 then Range := 2;
+          //if Ini.Difficulty = 1 then Range := 1;
+          //if Ini.Difficulty = 2 then Range := 0;
+          Range := 2 - Ini.Difficulty;
+
+          if abs(Lines[0].Line[S].Note[Count].Tone - AudioInputProcessor.Sound[CP].Tone) <= Range then
+          begin
+            AudioInputProcessor.Sound[CP].Tone := Lines[0].Line[S].Note[Count].Tone;
+
+            // Half size Notes Patch
+            NoteHit := true;
+            
+            MaxPoints := 10000;
+            if (Ini.LineBonus <> 0) then
+              MaxPoints := 9000;
+              
+            case Lines[0].Line[S].Note[Count].NoteType of
+              ntNormal:  Player[CP].Score := Player[CP].Score + MaxPoints / Lines[0].ScoreValue *
+                                             Lines[0].Line[S].Note[Count].Length;
+              ntGolden:  Player[CP].ScoreGolden := Player[CP].ScoreGolden + MaxPoints / Lines[0].ScoreValue *
+                                                   (Lines[0].Line[S].Note[Count].Length * 2);
+            end;
+
+            Player[CP].ScoreI := Floor(Player[CP].Score / 10) * 10;
+            Player[CP].ScoreGoldenI := Floor(Player[CP].ScoreGolden / 10) * 10;
+
+            Player[CP].ScoreTotalI := Player[CP].ScoreI + Player[CP].ScoreGoldenI + Player[CP].ScoreLineI;
+          end;
+
+        end; // operowanie
+      end; // for
+
+      // sprawdzanie czy to nowa nuta, czy przedluzenie
+      if S = SMax then
+      begin
+        New := true;
+        // if last has the same tone
+        if (Player[CP].IlNut > 0 ) and
+           (Player[CP].Note[Player[CP].HighNote].Tone = AudioInputProcessor.Sound[CP].Tone) and
+           (Player[CP].Note[Player[CP].HighNote].Start + Player[CP].Note[Player[CP].HighNote].Length = LineState.CurrentBeatD) then
+        begin
+          New := false;
+        end;
+
+        // if is not as new note to control "beacie" (TODO: translate polish "beacie")
+        for Count := 0 to Lines[0].Line[S].HighNote do
+        begin
+          if (Lines[0].Line[S].Note[Count].Start = LineState.CurrentBeatD) then
+            New := true;
+        end;
+
+        // dodawanie nowej nuty
+        if New then
+        begin
+          // New Note
+          Player[CP].IlNut := Player[CP].IlNut + 1;
+          Player[CP].HighNote := Player[CP].HighNote + 1;
+          SetLength(Player[CP].Note, Player[CP].IlNut);
+          Player[CP].Note[Player[CP].HighNote].Start   := LineState.CurrentBeatD;
+          Player[CP].Note[Player[CP].HighNote].Length := 1;
+          Player[CP].Note[Player[CP].HighNote].Tone     := AudioInputProcessor.Sound[CP].Tone; // Ton || TonDokl
+          Player[CP].Note[Player[CP].HighNote].Detekt  := LineState.MidBeat;
+
+          // Half Note Patch
+          Player[CP].Note[Player[CP].HighNote].Hit := NoteHit;
+
+          //Log.LogStatus('New Note ' + IntToStr(Gracz.Note[Gracz.HighNote].Start), 'NewBeat');
+        end
+        else
+        begin
+          // przedluzenie nuty
+          Player[CP].Note[Player[CP].HighNote].Length := Player[CP].Note[Player[CP].HighNote].Length + 1;
+        end;
+
+        // check for perfect note and then lit the star (on Draw)
+        for Count := 0 to Lines[0].Line[S].HighNote do
+        begin
+          if (Lines[0].Line[S].Note[Count].Start = Player[CP].Note[Player[CP].HighNote].Start) and
+             (Lines[0].Line[S].Note[Count].Length = Player[CP].Note[Player[CP].HighNote].Length) and
+             (Lines[0].Line[S].Note[Count].Tone = Player[CP].Note[Player[CP].HighNote].Tone) then
+          begin
+            Player[CP].Note[Player[CP].HighNote].Perfect := true;
+          end;
+        end;
+      end; // if S = SMax
+
+    end; // if moze
+  end; // for CP
+  //  Log.LogStatus('EndBeat', 'NewBeat');
+
+  //On Sentence End -> For LineBonus + SingBar
+  if (sDet >= low(Lines[0].Line)) and (sDet <= high(Lines[0].Line)) then
+  begin
+    if assigned( Sender ) and
+       ((Lines[0].Line[SDet].Note[Lines[0].Line[SDet].HighNote].Start + Lines[0].Line[SDet].Note[Lines[0].Line[SDet].HighNote].Length - 1) = LineState.CurrentBeatD) then
+    begin
+      Sender.onSentenceEnd(sDet);
+    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/UMusic.pas b/Game/Code/Classes/UMusic.pas
index c4123dd0..c389574b 100644
--- a/Game/Code/Classes/UMusic.pas
+++ b/Game/Code/Classes/UMusic.pas
@@ -14,19 +14,9 @@ uses
 type
   TNoteType = (ntFreestyle, ntNormal, ntGolden);
 
-  //http://paste.ubuntu-nl.org/51892/
-
-  TMelody = record
-    Path:       string;
-    Start:      integer;      // start of song in ms
-    IlNut:      integer;      // (TODO: Il = tone, Nut(a) = Note)
-    NoteLength: integer;
-  end;
-
   PLine = ^TLine;
   TLine = record
     Start:      integer;
-    StartNote:  integer;
     Lyric:      string;
     LyricWidth: real;
     End_:       integer;
@@ -40,10 +30,8 @@ type
       Start:      integer;
       Length:     integer;
       Tone:       integer;    // full range tone
-      ToneGamus:  integer;    // tone unified to one octave
       Text:       string;
-      FreeStyle:  boolean;
-      NoteType:   integer;    // normal-note: 1, golden-note: 2
+      NoteType:   TNoteType;
     end;
   end;
   ALine = array of TLine;     // (TODO: rename to TLineArray)
@@ -52,10 +40,10 @@ type
   TLines = record
     Current:    integer;      // for drawing of current line
     High:       integer;
-    Number:      integer;
+    Number:     integer;
     Resolution: integer;
     NotesGAP:   integer;
-    NoteType:   integer;
+    ScoreValue: integer;
     Line:       ALine;
   end;
 
@@ -288,9 +276,6 @@ type
   end;
 
 var // TODO : JB --- THESE SHOULD NOT BE GLOBAL
-  // music
-  Melody:   TMelody;
-
   // czesci z nutami;
   Lines:   array of TLines;
 
diff --git a/Game/Code/Classes/USong.pas b/Game/Code/Classes/USong.pas
index d6021811..60f5f0bf 100644
--- a/Game/Code/Classes/USong.pas
+++ b/Game/Code/Classes/USong.pas
@@ -126,23 +126,28 @@ type
       SongID:   Integer; //ID of this Song in the Song Database
       FolderID: Integer; //ID of the Folder containing this Song
       FileName: String;  //Filename of this Song w/o Path
-      FullPath: String;  //Path + Filename
-
-      Procedure ResetAttributes; virtual;  //Reset all Attributes of this object
-      Procedure ReadHeader; virtual;       //Reads Fileheader (Implemented by Child only)
-      Procedure ReadFile;   virtual;       //Reads complete File (Header + Notes) (Implemented by Child only)
-      Procedure WriteFile;  virtual;       //Writes complete File (Header + Notes) (Implemented by Child only)
-      Procedure ReadFromDB; virtual;       //Reads all available Information from DB
-      Function  CheckDB: Integer; virtual; //Checks DB for Song. Result > 0 SongExists (ID Returned)
-      Function  UpdateDB: Integer; virtual;//Writes all Header Information set in this Song Object to DB. Returns ID (Required when Updated first Time)
-    public
+      FilePath: String;  //Path of this Song
+
+      Procedure ResetAttributes; virtual;  		//Reset all Attributes of this object
+      Procedure ReadHeader; virtual; abstract;  //Reads Fileheader (Implemented by Child only)
+      Procedure ReadFile;   virtual; abstract;  //Reads complete File (Header + Notes) (Implemented by Child only)
+      Procedure WriteFile;  virtual; abstract;  //Writes complete File (Header + Notes) (Implemented by Child only)
+      Procedure ReadFromDB; virtual;       		//Reads all available Information from DB
+      Function  CheckDB: Integer; virtual; 		//Checks DB for Song. Result > 0 SongExists (ID Returned)
+      Function  UpdateDB: Integer; virtual;		//Writes all Header Information set in this Song Object to DB. Returns ID (Required when Updated first Time)
+      
+      // procedures to manage the lyrics
+      Procedure ResetLyrics;
+      Procedure AddLyricLine(const PlayerID: Integer; const StartBeat: Integer; const RelativeBeat: Integer = -1);
+      Procedure AddNote(const PlayerID: Integer; const NoteType: Char; const NoteStart, NoteLength, NoteTone: Integer; const NoteText: WideString);
+      Function SolmizatLyrics(const NoteTone: Integer; const NoteText: WideString): WideString;
+  public
       //Required Information
       Title:      widestring;
       Artist:     widestring;
 
       Mp3:        widestring; //Full Path to MP3
 
-      Text:       widestring;
       Creator:    widestring;
 
       Resolution: integer;
@@ -150,7 +155,6 @@ type
       GAP:        real; // in miliseconds
 
       Base    : array[0..1] of integer;
-      Rel     : array[0..1] of integer;
       Mult    : integer;
       MultBPM : integer;
 
@@ -189,14 +193,16 @@ implementation
 uses
   TextGL,
   UIni,
-  UMusic,  //needed for Lines
-  UMain;   //needed for Player
+  UMusic;  //needed for Lines
+
+var
+  RelativPosition: array [0..1] of Integer;
 
 constructor TSong.Create(const Path: String = ''; const FolderID: Integer = 0);
 begin
   If (Length(Path) > 0) AND (FolderID > 0) then
   begin //Read Song Infos from File or DB
-    FullPath := Path;
+    FilePath := ExtractFilePath(Path);
     FileName := ExtractFileName(Path);
     Self.FolderID := FolderID;
     SongID := CheckDB;
@@ -249,35 +255,42 @@ end;
 //--------
 Procedure TSong.ResetAttributes;
 begin
-  //to do
-end;
-
-//--------
-// Reads Fileheader (Implemented by Child only)
-//--------
-Procedure TSong.ReadHeader;
-begin
-  //Implented in Childs only!
+  SongID		:= 0;
+  FolderID		:= 0;
+  FileName		:= '';
+  FilePath		:= '';
+
+  Title			:= '';
+  Artist		:= '';
+  Mp3			:= '';
+  SetLength(BPM, 0);
+
+  Creator		:= '';
+  Resolution	:= 0;
+  GAP			:= 0;
+
+  Base[0]    	:= 100;
+  Base[1]		:= 100;
+  
+  Mult    		:= 1;
+  MultBPM 		:= 4;
+
+  Cover			:= '';
+  CoverID		:= 0;
+  Background	:= '';
+  Video			:= '';
+  VideoGAP		:= 0;
+
+  NotesGAP		:= 0;
+  Start			:= 0;
+  Finish		:= 0;
+  Relative		:= False;
+
+  Genre			:= '';
+  Edition		:= '';
+  Language		:= '';
 end;
 
-//--------
-// Reads complete File (Header + Notes) (Implemented by Child only)
-//--------
-Procedure TSong.ReadFile;
-begin
-  //Implented in Childs only!
-end;
-
-
-//--------
-// Writes complete File (Header + Notes) (Implemented by Child only)
-//--------
-Procedure TSong.WriteFile;
-begin
-  //Implented in Childs only!
-end;
-
-
 //--------
 // Reads all available Information from DB
 //--------
@@ -304,6 +317,154 @@ begin
   // to- do
 end;
 
+Procedure TSong.ResetLyrics;
+var
+  i: Integer;
+begin
+  for i := 0 to High(Lines) do
+  begin
+    SetLength(Lines[i].Line, 0);
+    Lines[i].High := -1;
+  end;
+  
+  for i := 0 to High(RelativPosition) do
+  begin
+    RelativPosition[i] := 0;
+  end;
+end;
+
+Procedure TSong.AddLyricLine(const PlayerID: Integer; const StartBeat: Integer; const RelativeBeat: Integer = -1);
+var
+  NewLineIdx: Integer;
+begin
+  NewLineIdx := High(Lines[PlayerID].Line) + 1;
+  
+  with Lines[PlayerID] do
+  begin
+    // recent added line is not the last of the song
+    if (NewLineIdx > 0) then
+      Lines[PlayerID].Line[NewLineIdx - 1].LastLine := False;
+    
+    // if last line, has no notes, ignore the new line (except for the RelativeBeat)
+    if (NewLineIdx < 1) OR (Lines[PlayerID].Line[NewLineIdx - 1].HighNote > -1) then
+    begin
+      // create lyric line and update references
+      SetLength(Line, NewLineIdx);
+      High :=   NewLineIdx;
+      Number := Number + 1;
+      with Line[High] do
+      begin  
+        // default values
+        TotalNotes := 0;
+        HighNote := -1;
+        LastLine := True;
+        BaseNote := 100;
+          
+        // set start beat count of this new line
+        Start := (RelativPosition[PlayerID] + StartBeat) * Mult;
+      end;
+    end;
+    
+    if Relative then
+    begin
+      if RelativeBeat >= 0 then
+        // if this is a relativ song, we have to update the relativ offset
+        RelativPosition[PlayerID] := (RelativPosition[PlayerID] + RelativeBeat) * Mult
+      else
+        RelativPosition[PlayerID] := (RelativPosition[PlayerID] + StartBeat) * Mult;
+    end;
+  end;
+end;  
+
+Procedure TSong.AddNote(const PlayerID: Integer; const NoteType: Char; const NoteStart, NoteLength, NoteTone: Integer; const NoteText: WideString);
+begin
+  if (High(Lines[PlayerID].Line) < 0) then
+    AddLyricLine(PlayerID, NoteStart, 0);
+  
+  with Lines[PlayerID].Line[Lines[PlayerID].High] do begin
+    // array of Notes expand to have space for new Note
+    HighNote := HighNote + 1;
+    TotalNotes := TotalNotes + 1;
+    SetLength(Note, HighNote + 1);
+    
+    with Note[HighNote] do
+    begin
+      Start := (RelativPosition[PlayerID] + NoteStart) * Mult;
+      Length := NoteLength * Mult;
+      Tone := NoteTone;
+      Text := SolmizatLyrics(NoteTone, NoteText);
+      Lyric := Lyric + Text;
+      
+      // identify lowest note of line
+      if Tone < BaseNote then
+        BaseNote := Tone;
+    end;
+    
+    case NoteType of
+      'F': Note[HighNote].NoteType := ntFreestyle;      
+      ':': Note[HighNote].NoteType := ntNormal;
+      '*': Note[HighNote].NoteType := ntGolden;
+    end;
+    
+    // calculate total score value
+    if (Note[HighNote].NoteType = ntNormal) then
+    begin
+      // normal notes
+      Lines[PlayerID].ScoreValue := Lines[PlayerID].ScoreValue + Note[HighNote].Length;
+      TotalNotes := TotalNotes + Note[HighNote].Length;
+    end    
+    else if (Note[HighNote].NoteType = ntGolden) then
+    begin
+      // golden notes
+      Lines[PlayerID].ScoreValue := Lines[PlayerID].ScoreValue + (Note[HighNote].Length * 2);
+      TotalNotes := TotalNotes + (Note[HighNote].Length * 2);
+    end;
+    
+    // finish of the line
+    End_ := Note[HighNote].Start + Note[HighNote].Length;
+  end; // with
+end;
+
+Function TSong.SolmizatLyrics(const NoteTone: Integer; const NoteText: WideString): WideString;
+begin
+  Result := NoteText;
+  
+  case Ini.Solmization of
+    1:  // european
+      case (NoteTone mod 12) of
+        0..1:  	Result := 'do ';
+        2..3:  	Result := 're ';
+        4:  	Result := 'mi ';
+        5..6:  	Result := 'fa ';
+        7..8:  	Result := 'sol ';
+        9..10:  Result := 'la ';
+        11:  	Result := 'si ';
+      end;
+    
+    2:  // japanese
+      case (NoteTone mod 12) of
+        0..1:  	Result := 'do ';
+        2..3:  	Result := 're ';
+        4:     	Result := 'mi ';
+        5..6:  	Result := 'fa ';
+        7..8:  	Result := 'so ';
+        9..10: 	Result := 'la ';
+        11:    	Result := 'shi ';
+      end;
+
+    3:  // american
+      case (NoteTone mod 12) of
+        0..1:  	Result := 'do ';
+        2..3:  	Result := 're ';
+        4:  	Result := 'mi ';
+        5..6:  	Result := 'fa ';
+        7..8:  	Result := 'sol ';
+        9..10:  Result := 'la ';
+        11:  	Result := 'ti ';
+      end;
+  end; // case Ini.Solmization
+end;
+
 
 {constructor TSong.create( const aFileName : WideString );
 begin
@@ -998,126 +1159,6 @@ begin
 
 end;
 
-procedure TSong.ParseNote(LineNumber: integer; TypeP: char; StartP, DurationP, NoteP: integer; LyricS: string);
-//var
-// Space:  boolean; // Auto Removed, Unused Variable
-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 Lines[LineNumber].Line[Lines[LineNumber].High] do begin
-    SetLength(Note, Length(Note) + 1);
-    IlNut := IlNut + 1;
-    HighNote := HighNote + 1;
-    Melody.IlNut := Melody.IlNut + 1;
-
-    Note[HighNote].Start := StartP;
-    if IlNut = 1 then begin
-      StartNote := Note[HighNote].Start;
-      if Lines[LineNumber].Number = 1 then
-        Start := -100;
-//        Start := Note[HighNote].Start;
-    end;
-
-    Note[HighNote].Length := DurationP;
-    Melody.NoteLength := Melody.NoteLength + Note[HighNote].Length;
-
-    // back to the normal system with normal, golden and now freestyle notes
-    case TypeP of
-      'F':  Note[HighNote].NoteType := 0;
-      ':':  Note[HighNote].NoteType := 1;
-      '*':  Note[HighNote].NoteType := 2;
-    end;
-
-    Lines[LineNumber].NoteType := Lines[LineNumber].NoteType + Note[HighNote].Length * Note[HighNote].NoteType;
-
-    Note[HighNote].Tone := NoteP;
-    if Note[HighNote].Tone < Base[LineNumber] then Base[LineNumber] := Note[HighNote].Tone;
-    Note[HighNote].ToneGamus := Note[HighNote].ToneGamus mod 12;
-
-    Note[HighNote].Text := Copy(LyricS, 2, 100);
-    Lyric := Lyric + Note[HighNote].Text;
-
-    if TypeP = 'F' then
-      Note[HighNote].FreeStyle := true;
-
-    End_ := Note[HighNote].Start + Note[HighNote].Length;
-  end; // with
-end;
-
-procedure TSong.NewSentence(LineNumberP: integer; Param1, Param2: integer);
-var
-I: Integer;
-begin
-
-  // stara czesc //Alter Satz //Update Old Part
-  Lines[LineNumberP].Line[Lines[LineNumberP].High].BaseNote := Base[LineNumberP];
-  Lines[LineNumberP].Line[Lines[LineNumberP].High].LyricWidth := glTextWidth(PChar(Lines[LineNumberP].Line[Lines[LineNumberP].High].Lyric));
-
-  //Total Notes Patch
-  Lines[LineNumberP].Line[Lines[LineNumberP].High].TotalNotes := 0;
-  for I := low(Lines[LineNumberP].Line[Lines[LineNumberP].High].Note) to high(Lines[LineNumberP].Line[Lines[LineNumberP].High].Note) do
-  begin
-    Lines[LineNumberP].Line[Lines[LineNumberP].High].TotalNotes := Lines[LineNumberP].Line[Lines[LineNumberP].High].TotalNotes + Lines[LineNumberP].Line[Lines[LineNumberP].High].Note[I].Length * Lines[LineNumberP].Line[Lines[LineNumberP].High].Note[I].NoteType;
-  end;
-  //Total Notes Patch End
-
-
-  // nowa czesc //Neuer Satz //Update New Part
-  SetLength(Lines[LineNumberP].Line, Lines[LineNumberP].Number + 1);
-  Lines[LineNumberP].High := Lines[LineNumberP].High + 1;
-  Lines[LineNumberP].Number := Lines[LineNumberP].Number + 1;
-  Lines[LineNumberP].Line[Lines[LineNumberP].High].HighNote := -1;
-
-  if self.Relative then
-  begin
-    Lines[LineNumberP].Line[Lines[LineNumberP].High].Start := Param1;
-    Rel[LineNumberP] := Rel[LineNumberP] + Param2;
-  end
-  else
-    Lines[LineNumberP].Line[Lines[LineNumberP].High].Start := Param1;
-
-  Lines[LineNumberP].Line[Lines[LineNumberP].High].LastLine := False;
-
-  Base[LineNumberP] := 100; // high number
-end;
-
 procedure TSong.clear();
 begin
   //Main Information
diff --git a/Game/Code/Classes/USong_TextFile.pas b/Game/Code/Classes/USong_TextFile.pas
new file mode 100644
index 00000000..a3e605de
--- /dev/null
+++ b/Game/Code/Classes/USong_TextFile.pas
@@ -0,0 +1,86 @@
+unit USong_TextFile;
+
+interface
+
+{$IFDEF FPC}
+  {$MODE Delphi}
+{$ENDIF}
+
+{$I switches.inc}
+
+uses
+  Classes,
+  SysUtils,
+  USong;
+
+type
+  {*******************
+    Child of the new TSong class.
+    implements filehandling to load a song from a text file
+   *******************}
+  TSong_TextFile = class(TSong)
+    protected
+      SongFile: TextFile;
+      
+      Function OpenSongFile: Boolean;
+      Function IsDataAvailable: Boolean;
+      Function GetNextLine(): String;
+      Procedure CloseSongFile;
+  end;
+  
+implementation
+
+uses
+  ULog;
+
+//--------
+// Open the SongFile
+//--------
+Function TSong_TextFile.OpenSongFile: Boolean;
+begin
+  Result := False;
+  
+  if not FileExists(FilePath + FileName) then
+    Log.LogError('File does not exsist', FilePath + FileName)
+  else
+  begin
+    try
+      AssignFile(SongFile, FilePath + FileName);
+      Reset(SongFile);
+      Result := True;
+    except
+      Log.LogError('Faild to open file', FilePath + FileName)
+    end;
+  end;
+end;
+
+//--------
+// More data in songfile available?
+//--------
+Function TSong_TextFile.IsDataAvailable: Boolean;
+begin
+  Result := not eof(SongFile);
+end;
+
+//--------
+// Returns the next line from the SongFile
+//--------
+Function TSong_TextFile.GetNextLine(): String;
+begin
+  ReadLn(SongFile, Result);
+  Result := Trim(Result);
+end;
+
+//--------
+// Close the SongFile
+//--------
+Procedure TSong_TextFile.CloseSongFile;
+begin
+  try
+    CloseFile(SongFile);
+  except
+    Log.LogError('Error closing file', FilePath + FileName);
+  end;
+end;
+
+end.
\ No newline at end of file
diff --git a/Game/Code/Classes/USong_Txt.pas b/Game/Code/Classes/USong_Txt.pas
new file mode 100644
index 00000000..23171517
--- /dev/null
+++ b/Game/Code/Classes/USong_Txt.pas
@@ -0,0 +1,438 @@
+unit USong_Txt;
+
+interface
+
+{$IFDEF FPC}
+  {$MODE Delphi}
+{$ENDIF}
+
+{$I switches.inc}
+
+
+uses
+  Classes,
+  SysUtils,
+  USong_TextFile;
+
+type
+  {*******************
+    Child of the new TSong class.
+    implements methods to load a song form a txt file (ultrastar file format)
+   *******************}
+  TSong_Txt = class(TSong_TextFile)
+    public
+      Procedure ReadHeader; override;        //Reads Fileheader (Implemented by Child only)
+      Procedure ReadFile; override;          //Reads complete File (Header + Notes) (Implemented by Child only)
+      Procedure WriteFile; override;         //Writes complete File (Header + Notes) (Implemented by Child only)
+  end;  
+  
+implementation
+
+uses
+  UMusic,
+  UMain,
+  UPlatform,
+  ULog;
+
+type
+  TTags = (ArtistTag, TitleTag, Mp3Tag, BPMTag);
+  
+//--------
+// Reads Fileheader
+//--------
+Procedure TSong_Txt.ReadHeader;
+var
+  started: Boolean;
+  line, key, value: String;
+  FloatValue: Real;
+  FoundTags: Set of TTags;
+  
+  // splits a headerline in key an value
+  // returns true on success and false if this is not a valid headerline
+  Function SplitHeaderLine(const Line: String; var Key, Value: String): Boolean;
+  var
+    idx: Integer;
+  begin
+    Result := False;
+    
+    idx := Pos(':', Line);
+    if idx > 0 then
+    begin
+      Key := Uppercase(Trim(Copy(Line, 2, idx - 2))); //Uppercase is for Case Insensitive Checks
+      Value := Trim(Copy(Line, idx + 1,Length(Line) - idx));
+      
+      if (Length(Key) > 0) AND (Length(Value) > 0) then
+        Result := True
+    end;
+  end;
+  
+  function song_StrToFloat(const aValue : String): Extended;
+  var
+    lValue: String;
+  begin
+    lValue := aValue;
+
+    if (Pos(',', lValue) <> 0) then
+      lValue[Pos(',', lValue)] := '.';
+
+    Result := StrToFloatDef(lValue, 0);
+  end;
+  
+begin
+  if not OpenSongFile then
+    exit;
+  
+  started := False;
+  FoundTags := [];
+  
+  while isDataAvailable do
+  begin
+    line := GetNextLine();
+    
+    // break if header has finished
+    if started AND ((Length(line) > 0) AND (not (line[1] = '#'))) then
+      break;
+    
+    // skip invalid lines at beginning
+    if (not started) AND (line[1] = '#') then
+      started := True;
+    
+    // parse line
+    if started then
+    begin
+      if (Length(line) > 0) AND not SplitHeaderLine(line, key, value) then
+      begin
+        Log.LogError('Invalide line in Header of file: ' + FilePath + FileName);
+        Log.LogError('Line: ' + line);
+        break;
+      end;
+      
+      {$IFDEF UTF8_FILENAMES}
+      if ((Key = 'MP3') or (Key = 'BACKGROUND') or (Key = 'COVER') or (Key = 'VIDEO')) then
+        Value := Utf8Encode(Value);
+      {$ENDIF}
+
+      //Title
+      if (Key = 'TITLE') then
+      begin
+        Title := Value;
+        FoundTags := FoundTags + [TitleTag];
+        continue;
+      end;
+
+      //Artist
+      if (Key = 'ARTIST') then
+      begin
+        Artist := Value;
+        FoundTags := FoundTags + [ArtistTag];
+        continue;
+      end;
+
+      //MP3 File //Test if Exists
+      if (Key = 'MP3') then
+      begin
+        if FileExists(FilePath + Value) then
+        begin
+          Mp3 := FilePath + Value;
+          FoundTags := FoundTags + [Mp3Tag];
+        end
+        else
+          Log.LogError('Can''t find MP3 File: ' + FilePath + Value + ' in Song: ' + FilePath + FileName);
+        continue;
+      end;    
+
+      //Beats per Minute
+      if (Key = 'BPM') then
+      begin
+        FloatValue := song_StrtoFloat( Value ) * Mult * MultBPM;
+
+        if FloatValue <> 0 then
+        begin
+          SetLength(BPM, 1);
+          BPM[0].StartBeat := 0;
+          BPM[0].BPM := floatValue;
+          FoundTags := FoundTags + [BPMTag];
+        end;
+        
+        continue;
+      end;
+
+      //---------
+      //Additional Header Information
+      //---------
+
+      // Gap
+      if (Key = 'GAP') then
+      begin
+        GAP := song_StrtoFloat( Value );
+        
+        continue;
+      end;
+
+      //Cover Picture
+      if (Key = 'COVER') then
+      begin
+        if FileExists(FilePath + Value) then
+          Cover := FilePath + Value
+        else
+          Log.LogError('Can''t find Cover File: ' + FilePath + Value + ' in Song: ' + FilePath + FileName);
+        
+        continue;  
+      end;
+
+      //Background Picture
+      if (Key = 'BACKGROUND') then
+      begin
+        if FileExists(FilePath + Value) then
+          Background := FilePath + Value
+        else
+          Log.LogError('Can''t find Background File: ' + FilePath + Value + ' in Song: ' + FilePath + FileName);
+        
+        continue;
+      end;
+
+      // Video File
+      if (Key = 'VIDEO') then
+      begin
+        if FileExists(FilePath + Value) then
+          Video := FilePath + Value
+        else
+          Log.LogError('Can''t find Video File: ' + FilePath + Value + ' in Song: ' + FilePath + FileName);
+        
+        continue;
+      end;
+
+      // Video Gap
+      if (Key = 'VIDEOGAP') then
+      begin
+        VideoGAP := song_StrtoFloat(Value);
+        continue;
+      end;
+
+      //Genre Sorting
+      if (Key = 'GENRE') then
+      begin
+        Genre := Value;
+        continue;
+      end;
+
+      //Edition Sorting
+      if (Key = 'EDITION') then
+      begin
+        Edition := Value;
+        continue;
+      end;
+
+      //Creator Tag
+      if (Key = 'CREATOR') then
+      begin
+        Creator := Value;
+        continue;
+      end;
+
+      //Language Sorting
+      if (Key = 'LANGUAGE') then
+      begin
+        Language := Value;
+        continue;
+      end;
+
+      // Song Start
+      if (Key = 'START') then
+      begin
+        Start := song_StrtoFloat( Value );
+        continue;
+      end;
+
+      // Song Ending
+      if (Key = 'END') then
+      begin
+        TryStrtoInt(Value, Finish);
+        continue;
+      end;
+
+      // Resolution
+      if (Key = 'RESOLUTION') then
+      begin
+        TryStrtoInt(Value, Resolution);
+        continue;
+      end;
+
+      // Notes Gap
+      if (Key = 'NOTESGAP') then
+      begin
+        TryStrtoInt(Value, NotesGAP);
+        continue;
+      end;
+      
+      // Relative Notes
+      if (Key = 'RELATIVE') AND (uppercase(Value) = 'YES') then
+      begin  
+        Relative := True;
+        continue;
+      end;
+      
+    end; // if started
+  end; // while
+  
+  if Cover = '' then
+  begin
+    Cover := platform.FindSongFile(FilePath, '*[CO].jpg');
+  end;
+  
+  // check if all required infos are given
+  if not (BPMTag in FoundTags) then
+    Log.LogError('BPM Tag missing: ' + FilePath + FileName);
+  
+  if not (MP3Tag in FoundTags) then
+    Log.LogError('MP3 Tag missing or invalid file: ' + FilePath + FileName);
+  
+  if not (ArtistTag in FoundTags) then
+    Log.LogError('Artist Tag missing: ' + FilePath + FileName);
+  
+  if not (TitleTag in FoundTags) then
+    Log.LogError('Title Tag missing: ' + FilePath + FileName);
+  
+  CloseSongFile();
+end;
+
+//--------
+// Reads complete File (Header + Notes)
+//--------
+Procedure TSong_Txt.ReadFile;
+var
+  Line: String;
+  NotesRead: Boolean;
+  Values: TStringList;
+  
+  Procedure ParseDelimited(const StringList: TStringList; const Value: String; const Delimiter: String; const MaxParts: Integer);
+  var
+    idx: Integer;
+    Source: String;
+    Delta: Integer;
+  begin
+    Delta := Length(Delimiter);
+    Source := Value;
+    StringList.BeginUpdate;
+    StringList.Clear;
+    try
+      while ((Pos(Delimiter, Source) > 0) OR ((MaxParts > 0) AND  (StringList.count+1 >= MaxParts))) do
+      begin
+        idx := Pos(Delimiter, Source);
+        StringList.Add(Copy(Source,0,idx-1));
+        Source := Copy(Source,idx+Delta, MaxInt);
+      end;
+             
+      if (Length(Source) > 0) then
+        StringList.Add(Source);
+    finally
+      StringList.EndUpdate;
+    end;
+  end;
+  
+begin
+  // read Header
+  Self.ReadHeader;
+  
+  OpenSongFile();
+  
+  ResetLyrics;
+  NotesRead := False;
+  
+  while isDataAvailable do
+  begin
+    line := GetNextLine();
+    
+    // end of song
+    if (line[1] = 'E') then
+      break;
+    
+    // skip invalid lines
+    if (line[1] <> ':') OR (line[1] <> 'F') OR (line[1] <> '*') OR (line[1] <> '-') OR (line[1] <> 'B') then
+      continue;
+    
+    // aktuelle Zeile in einzelne Werte aufteilen
+    Values := TStringList.Create;
+    ParseDelimited(Values, line, ' ', 5);
+    
+    try
+      if (line[1] = '-') then
+        if (not NotesRead) then
+          // skip newline if no notes before
+          continue
+        else
+        begin
+          // new lyric line
+          // param count: 1 (relative: 2)
+          
+          if (Values.Count > 2) then
+          // relativ offset  
+          begin
+            // P1
+            AddLyricLine(0, StrToInt(Values[1]), StrToInt(Values[2]));
+              
+            // P2
+            if Length(Player) = 2 Then
+              AddLyricLine(1, StrToInt(Values[1]), StrToInt(Values[2]))
+          end
+          else
+          begin
+            AddLyricLine(0, StrToInt(Values[1]));
+              
+            // P2
+            if Length(Player) = 2 then
+              AddLyricLine(1, StrToInt(Values[1]))
+          end;
+        end;
+    
+      // new BPM set
+      if (line[1] = 'B') then
+      begin
+        // param count: 2
+        {SetLength(BPM, Length(BPM) + 1);
+        Read(SongFile, BPM[High(BPM)].StartBeat);
+        BPM[High(BPM)].StartBeat := BPM[High(BPM)].StartBeat + Rel[0];
+
+        ReadLn(SongFile, Text);
+        BPM[High(BPM)].BPM := StrToFloat(Text);
+        BPM[High(BPM)].BPM := BPM[High(BPM)].BPM * Mult * MultBPM;}
+      end;
+    
+      if (line[1] = ':') OR (line[1] = '*') OR (line[1] = 'F') then
+      begin
+        // param count: 4
+        if (Values.Count < 5) then
+          Log.LogError('Error parsing line: ' + line, 'Not enough arguments.')
+        else
+        begin
+          // Check for ZeroNote
+          if StrToInt(Values[2]) = 0 then
+            Log.LogError('Found ZeroNote: "' + line + '" -> Note ignored!')
+          else
+            begin
+              // P1
+              AddNote(0, line[1], StrToInt(Values[1]), StrToInt(Values[2]), StrToInt(Values[3]), Values[4]);
+          
+              // P2
+              if Length(Player) = 2 then
+                AddNote(1, line[1], StrToInt(Values[1]), StrToInt(Values[2]), StrToInt(Values[3]), Values[4]);
+            end;
+        end;
+      end;
+    except
+      on E : Exception do
+        Log.LogError('Error parsing line: ' + line, E.ClassName + ': ' + E.Message);
+    end;
+  end;
+  
+  CloseSongFile();
+end;
+
+//--------
+// Writes complete File (Header + Notes)
+//--------
+Procedure TSong_Txt.WriteFile;
+begin
+end;
+
+end.
\ No newline at end of file
-- 
cgit v1.2.3