unit UPliki;

interface

uses USongs, SysUtils, ULog, UMusic;

procedure InitializePaths;
function ReadHeader(var Song: TSong): boolean;
function SkanujPlik(var Song: TSong): boolean;
procedure CzyscNuty;
function WczytajCzesci(Name: string): boolean;
function SaveSong(Song: TSong; Czesc: TCzesci; Name: string; Relative: boolean): boolean;
function SaveSongDebug(Song: TSong; Czesc: TCzesci; Name: string; Relative: boolean): boolean;

var
  GamePath:         string;
  SoundPath:        string;
  SongPath:         string;
  LogPath:          string;
  ThemePath:        string;
  ScreenshotsPath:  string;
  CoversPath:       string;
  LanguagesPath:    string;
  PluginPath:       string;
  PlayListPath:     string;

  Plik:       TextFile;   // all procedures in this unit operates on this file
  PlikC:      char;
  Lineno:   integer;

  // variables available for all procedures
  Base:       array[0..1] of integer;
  Rel:        array[0..1] of integer;
  Mult:     integer;
  MultBPM:  integer;

implementation
uses TextGL, UIni, UMain, math;

procedure InitializePaths;
begin
  GamePath :=   ExtractFilePath(ParamStr(0));
  SoundPath :=  GamePath + 'Sounds\';
  SongPath :=   GamePath + 'Songs\';
  LogPath := GamePath;
  ThemePath := GamePath + 'Themes\';
  ScreenshotsPath := GamePath + 'Screenshots\';
  CoversPath := GamePath + 'Covers\';
  LanguagesPath := GamePath + 'Languages\';
  //Modi Loader
  PluginPath := GamePath + 'Plugins\';

  PlaylistPath := GamePath + 'Playlists\';

  DecimalSeparator := ',';
end;

function ReadHeader(var Song: TSong): boolean;
var
  TempC:    char;
  Tekst:    string;
  Done:     integer;
begin
  // clear
  Song.Title := '';
  Song.Artist := '';
  Song.Genre := 'Unknown';
  Song.Edition := 'Unknown';
  Song.Language := 'Unknown'; //Language Patch
  Song.Mp3 := '';
  Song.BPM := 0;
  Song.GAP := 0;
  Song.Start := 0;
  Song.Finish := 0;
  Song.Background := '';
  Song.Video := '';
  Song.VideoGAP := 0;
  Song.NotesGAP := 0;
  Song.Resolution := 4;

  //Creator Patch
  Song.Creator := '';

  Done := 0;

  //Editor Error Reporting Hack
  LineNo := 0;
  try

  // read
  Read(Plik, PlikC);
  while (PlikC = '#') do begin
    ReadLn(Plik, Tekst);

    //Editor Error Reporting Hack
    Inc (LineNo);

    //Header Improvements Patch

    if UpperCase(Copy(Tekst, 1, 6)) = 'TITLE:' then begin
      Delete(Tekst, 1, 6);
      Song.Title := Trim(Tekst);
      Tekst := '';
      Done := Done or 1;
    end

    else if UpperCase(Copy(Tekst, 1, 7)) = 'ARTIST:' then begin
      Delete(Tekst, 1, 7);
      Song.Artist := Trim(Tekst);
      Tekst := '';
      Done := Done or 2;
    end

    else if UpperCase(Copy(Tekst, 1, 4)) = 'MP3:' then begin
      Delete(Tekst, 1, 4);
      Song.Mp3 := Trim(Tekst);
      Tekst := '';
      Done := Done or 4;
    end

    else if UpperCase(Copy(Tekst, 1, 8)) = 'CREATOR:' then begin // this goes for edit
      Delete(Tekst, 1, 8);
      Song.Creator := Trim(Tekst);
      Tekst := '';
    end

    else if UpperCase(Copy(Tekst, 1, 6)) = 'GENRE:' then begin // this goes for edit
      Delete(Tekst, 1, 6);
      Song.Genre := Trim(Tekst);
      Tekst := '';
    end

    else if UpperCase(Copy(Tekst, 1, 8)) = 'EDITION:' then begin // this goes for edit
      Delete(Tekst, 1, 8);
      Song.Edition := Trim(Tekst);
      Tekst := '';
    end

    else if UpperCase(Copy(Tekst, 1, 9)) = 'LANGUAGE:' then begin // this goes for edit
      Delete(Tekst, 1, 9);
      Song.Language := Trim(Tekst);
      Tekst := '';
    end

    else if UpperCase(Copy(Tekst, 1, 6)) = 'COVER:' then begin
      Delete(Tekst, 1, 6);
      Song.Cover := Trim(Tekst);
      Tekst := '';
    end

    else if UpperCase(Copy(Tekst, 1, 11)) = 'BACKGROUND:' then begin
      Delete(Tekst, 1, 11);
      Song.Background := Trim(Tekst);
      Tekst := '';
    end

    else if UpperCase(Copy(Tekst, 1, 6)) = 'VIDEO:' then begin
      Delete(Tekst, 1, 6);
      Song.Video := Trim(Tekst);
      Tekst := '';
    end

    else if UpperCase(Copy(Tekst, 1, 9)) = 'VIDEOGAP:' then begin
      Delete(Tekst, 1, 9);

      //Change . to , Mod by Whiteshark :P
      if (Pos('.',Tekst) <> 0) then
      begin
        Tekst[Pos('.',Tekst)] := ',';
        //Little Annonce for the User
        Log.LogError('VideoGap Seperator wrong in SongHeader: ' + Song.FileName + ' [Corrected for this Session]');
      end;

      Song.VideoGAP := StrToFloat(Tekst);
      Tekst := ''
    end

    else if UpperCase(Copy(Tekst, 1, 9)) = 'NOTESGAP:' then begin
      Delete(Tekst, 1, 9);
      Song.NotesGAP := StrToInt(Tekst);
      Tekst := ''
    end

    else if UpperCase(Copy(Tekst, 1, 9)) = 'RELATIVE:' then begin
      Delete(Tekst, 1, 9);
      if LowerCase(Tekst) = 'yes' then Song.Relative := true;
    end

    else if UpperCase(Copy(Tekst, 1, 6)) = 'START:' then begin
      Delete(Tekst, 1, 6);
      Song.Start := StrToFloat(Tekst);
//      Muzyka.Start := StrToInt(Tekst);
    end

    else if UpperCase(Copy(Tekst, 1, 4)) = 'END:' then begin
      Delete(Tekst, 1, 4);
      Song.Finish := StrToInt(Tekst);
    end

    else if UpperCase(Copy(Tekst, 1, 11)) = 'RESOLUTION:' then begin
      Delete(Tekst, 1, 11);
      Song.Resolution := StrToInt(Tekst);
    end

    else if UpperCase(Copy(Tekst, 1, 4)) = 'BPM:' then begin
      Delete(Tekst, 1, 4);

//      Muzyka.BPMOld := StrToFloat(Tekst) * Mult * MultBPM; // old system

      (* new system with variable BPM *)
//      Muzyka.BPMOld := 50;

      //Change . to , Mod by Whiteshark :P
      if (Pos('.',Tekst) <> 0) then
      begin
        Tekst[Pos('.',Tekst)] := ',';
        //Little Annonce for the User
        Log.LogError('BPM Seperator wrong in SongHeader: ' + Song.FileName + ' [Corrected for this Session]');
      end;

      SetLength(Song.BPM, 1);
      Song.BPM[0].StartBeat := 0;
      Song.BPM[0].BPM := StrToFloat(Tekst) * Mult * MultBPM;
      Tekst := '';
      Done := Done or 8;
    end

    else if UpperCase(Copy(Tekst, 1, 4)) = 'GAP:' then begin
      Delete(Tekst, 1, 4);
      Song.GAP := StrToFloat(Tekst);
     Tekst := '';
//      Muzyka.GAP := StrToFloat(Tekst);
//      Done := Done or 16;
    end;

    //Header Improvements Patch Ende

    Read(Plik, PlikC);
  end;

  //Editor Error Reporting Hack
  except //An Error happened<- bad english :P
  Log.LogError('An Error occured reading Line ' + inttostr(LineNo) + ' from SongHeader: ' + Song.FileName);
  Halt;
  end;
  //Editor Error Reporting Hack End

  if Song.Background = '' then begin
    Song.Background := Songs.FindSongFile(Song.Path, '*[BG].jpg');
  end;

  if (Done and 15) = 15 then Result := true
  else Result := false;
end;

function SkanujPlik(var Song: TSong): boolean;
var
  Done:     integer;
  Tekst:    string;
  C:        integer; // category
  P:        integer; // position
begin
//  try
  AssignFile(Plik, Song.Path + Song.FileName);
  Reset(Plik);

  Result := ReadHeader(Song);

{  ReadLn(Plik, Tekst);
  while (Copy(Tekst, 1, 1) = '#') do begin
    if Copy(Tekst, 1, 10) = '#CATEGORY:' then begin
      Delete(Tekst, 1, 10);

      Trim(Tekst);
      while (Length(Tekst) > 0) do begin
        C := Length(Song.Category);
        SetLength(Song.Category, C+1);

        P := Pos(',', Tekst);
        if P = 0 then P := Length(Tekst);
        Song.Category[C] := Copy(Tekst, 1, P);

        Delete(Tekst, 1, P);
        Trim(Tekst);
      end;

    end;}


end;

procedure CzyscNuty;
var
  Pet:  integer;
begin
  SetLength(Czesci, Length(Player));
  SetLength(AktSong.BPM, 0);
  for Pet := 0 to High(Player) do begin
    SetLength(Czesci[Pet].Czesc, 1);
    SetLength(Czesci[Pet].Czesc[0].Nuta, 0);
    Czesci[Pet].Czesc[0].Lyric := '';
    Czesci[Pet].Czesc[0].LyricWidth := 0;
    Player[pet].Score := 0;
    Player[pet].IlNut := 0;
    Player[pet].HighNut := -1;
  end;
end;

procedure DodajNute(NrCzesci: integer; TypeP: char; StartP, DurationP, NoteP: integer; LyricS: string);
var
  Space:  boolean;
begin
  case Ini.Solmization of
    1:  // european
      begin
        case (NoteP mod 12) of
          0..1:  LyricS := ' do ';
          2..3:  LyricS := ' re ';
          4:  LyricS := ' mi ';
          5..6:  LyricS := ' fa ';
          7..8:  LyricS := ' sol ';
          9..10:  LyricS := ' la ';
          11:  LyricS := ' si ';
        end;
      end;
    2:  // japanese
      begin
        case (NoteP mod 12) of
          0..1:  LyricS := ' do ';
          2..3:  LyricS := ' re ';
          4:  LyricS := ' mi ';
          5..6:  LyricS := ' fa ';
          7..8:  LyricS := ' so ';
          9..10:  LyricS := ' la ';
          11:  LyricS := ' shi ';
        end;
      end;
    3:  // american
      begin
        case (NoteP mod 12) of
          0..1:  LyricS := ' do ';
          2..3:  LyricS := ' re ';
          4:  LyricS := ' mi ';
          5..6:  LyricS := ' fa ';
          7..8:  LyricS := ' sol ';
          9..10:  LyricS := ' la ';
          11:  LyricS := ' ti ';
        end;
      end;
  end; // case

//  Log.LogStatus('Czesc: ' + IntToStr(Czesci[NrCzesci].High), 'DodajNute');
//  Log.LogStatus('Dodano: [' + IntToStr(NrCzesci) + '] ' + IntToStr(StartP) + ' '
//    + IntToStr(DurationP) + ' '+ IntToStr(NoteP) + ' ' + LyricS, 'DodajNute');

{  Delete(LyricS, 1, 1);
  Space := false;
  if Copy(LyricS, Length(LyricS), 1) = ' ' then begin
    Space := true;
    Delete(LyricS, Length(LyricS), 1);
  end;
  if LyricS = 'a' then LyricS := chr($B1);
  if LyricS = 'i' then LyricS := chr($B2);
  if LyricS = 'u' then LyricS := chr($B3);
  if LyricS = 'e' then LyricS := chr($B4);
  if LyricS = 'o' then LyricS := chr($B5);

  if LyricS = 'ka' then LyricS := chr($B6);
  if LyricS = 'ki' then LyricS := chr($B7);
  if LyricS = 'ku' then LyricS := chr($B8);
  if LyricS = 'ke' then LyricS := chr($B9);
  if LyricS = 'ko' then LyricS := chr($BA);

  if LyricS = 'ga' then LyricS := chr($B6) + chr($DE);
  if LyricS = 'gi' then LyricS := chr($B7) + chr($DE);
  if LyricS = 'gu' then LyricS := chr($B8) + chr($DE);
  if LyricS = 'ge' then LyricS := chr($B9) + chr($DE);
  if LyricS = 'go' then LyricS := chr($BA) + chr($DE);

  if LyricS = 'sa' then LyricS := chr($BB);
  if LyricS = 'shi' then LyricS := chr($BC);
  if LyricS = 'su' then LyricS := chr($BD);
  if LyricS = 'se' then LyricS := chr($BE);
  if LyricS = 'so' then LyricS := chr($BF);

  if LyricS = 'za' then LyricS := chr($BB) + chr($DE);
  if LyricS = 'ji' then LyricS := chr($BC) + chr($DE);
  if LyricS = 'zu' then LyricS := chr($BD) + chr($DE);
  if LyricS = 'ze' then LyricS := chr($BE) + chr($DE);
  if LyricS = 'zo' then LyricS := chr($BF) + chr($DE);

  if LyricS = 'ta' then LyricS := chr($C0);
  if LyricS = 'chi' then LyricS := chr($C1);
  if LyricS = 'tsu' then LyricS := chr($C2);
  if LyricS = 'te' then LyricS := chr($C3);
  if LyricS = 'to' then LyricS := chr($C4);

  if LyricS = 'da' then LyricS := chr($C0) + chr($DE);
//  if LyricS = 'ji' then LyricS := chr($C1) + chr($DE);
//  if LyricS = 'zu' then LyricS := chr($C2) + chr($DE);
  if LyricS = 'de' then LyricS := chr($C3) + chr($DE);
  if LyricS = 'do' then LyricS := chr($C4) + chr($DE);

  if LyricS = 'na' then LyricS := chr($C5);
  if LyricS = 'ni' then LyricS := chr($C6);
  if LyricS = 'nu' then LyricS := chr($C7);
  if LyricS = 'ne' then LyricS := chr($C8);
  if LyricS = 'no' then LyricS := chr($C9);

  if LyricS = 'ha' then LyricS := chr($CA);
  if LyricS = 'hi' then LyricS := chr($CB);
  if LyricS = 'hu' then LyricS := chr($CC);
  if LyricS = 'he' then LyricS := chr($CD);
  if LyricS = 'ho' then LyricS := chr($CE);

  if LyricS = 'ba' then LyricS := chr($CA) + chr($DE);
  if LyricS = 'bi' then LyricS := chr($CB) + chr($DE);
  if LyricS = 'bu' then LyricS := chr($CC) + chr($DE);
  if LyricS = 'be' then LyricS := chr($CD) + chr($DE);
  if LyricS = 'bo' then LyricS := chr($CE) + chr($DE);

  if LyricS = 'pa' then LyricS := chr($CA) + chr($DF);
  if LyricS = 'pi' then LyricS := chr($CB) + chr($DF);
  if LyricS = 'pu' then LyricS := chr($CC) + chr($DF);
  if LyricS = 'pe' then LyricS := chr($CD) + chr($DF);
  if LyricS = 'po' then LyricS := chr($CE) + chr($DF);

  if LyricS = 'ma' then LyricS := chr($CF);
  if LyricS = 'mi' then LyricS := chr($D0);
  if LyricS = 'mu' then LyricS := chr($D1);
  if LyricS = 'me' then LyricS := chr($D2);
  if LyricS = 'mo' then LyricS := chr($D3);

  if LyricS = 'ya' then LyricS := chr($D4);
  if LyricS = 'yu' then LyricS := chr($D5);
  if LyricS = 'yo' then LyricS := chr($D6);

  if LyricS = 'ra' then LyricS := chr($D7);
  if LyricS = 'ri' then LyricS := chr($D8);
  if LyricS = 'ru' then LyricS := chr($D9);
  if LyricS = 're' then LyricS := chr($DA);
  if LyricS = 'ro' then LyricS := chr($DB);

  if LyricS = 'wa' then LyricS := chr($DC);
  if LyricS = 'n' then LyricS := chr($DD);

  LyricS := ' ' + LyricS;
  if Space then LyricS := LyricS + ' ';}



  with Czesci[NrCzesci].Czesc[Czesci[NrCzesci].High] do begin
    SetLength(Nuta, Length(Nuta) + 1);
    IlNut := IlNut + 1;
    HighNut := HighNut + 1;
    Muzyka.IlNut := Muzyka.IlNut + 1;

    Nuta[HighNut].Start := StartP;
    if IlNut = 1 then begin
      StartNote := Nuta[HighNut].Start;
      if Czesci[NrCzesci].Ilosc = 1 then
        Start := -100;
//        Start := Nuta[HighNut].Start;
    end;

    Nuta[HighNut].Dlugosc := DurationP;
    Muzyka.DlugoscNut := Muzyka.DlugoscNut + Nuta[HighNut].Dlugosc;

    // back to the normal system with normal, golden and now freestyle notes
    case TypeP of
      'F':  Nuta[HighNut].Wartosc := 0;
      ':':  Nuta[HighNut].Wartosc := 1;
      '*':  Nuta[HighNut].Wartosc := 2;
    end;
    Czesci[NrCzesci].Wartosc := Czesci[NrCzesci].Wartosc + Nuta[HighNut].Dlugosc * Nuta[HighNut].Wartosc;

    Nuta[HighNut].Ton := NoteP;
    if Nuta[HighNut].Ton < Base[NrCzesci] then Base[NrCzesci] := Nuta[HighNut].Ton;
    Nuta[HighNut].TonGamy := Nuta[HighNut].TonGamy mod 12;

    Nuta[HighNut].Tekst := Copy(LyricS, 2, 100);
    Lyric := Lyric + Nuta[HighNut].Tekst;

    if TypeP = 'F' then
      Nuta[HighNut].FreeStyle := true;

    Koniec := Nuta[HighNut].Start + Nuta[HighNut].Dlugosc;
  end; // with
end;

procedure NewSentence(NrCzesciP: integer; Param1, Param2: integer);
var
I: Integer;
begin
//  Log.LogStatus('IlCzesci: ' + IntToStr(Czesci[NrCzesciP].Ilosc), 'NewSentece');
//  Log.LogStatus('Dane: ' + IntToStr(NrCzesciP) + ' ' + IntToStr(Param1) + ' ' + IntToStr(Param2) , 'NewSentece');

  // stara czesc //Alter Satz //Update Old Part
  Czesci[NrCzesciP].Czesc[Czesci[NrCzesciP].High].BaseNote := Base[NrCzesciP];
  Czesci[NrCzesciP].Czesc[Czesci[NrCzesciP].High].LyricWidth := glTextWidth(PChar(Czesci[NrCzesciP].Czesc[Czesci[NrCzesciP].High].Lyric));

  //Total Notes Patch
  Czesci[NrCzesciP].Czesc[Czesci[NrCzesciP].High].TotalNotes := 0;
  for I := low(Czesci[NrCzesciP].Czesc[Czesci[NrCzesciP].High].Nuta) to high(Czesci[NrCzesciP].Czesc[Czesci[NrCzesciP].High].Nuta) do
  begin
    Czesci[NrCzesciP].Czesc[Czesci[NrCzesciP].High].TotalNotes := Czesci[NrCzesciP].Czesc[Czesci[NrCzesciP].High].TotalNotes + Czesci[NrCzesciP].Czesc[Czesci[NrCzesciP].High].Nuta[I].Dlugosc * Czesci[NrCzesciP].Czesc[Czesci[NrCzesciP].High].Nuta[I].Wartosc;
  end;
  //Log.LogError('Total Notes(' + inttostr(Czesci[NrCzesciP].High) +'): ' + inttostr(Czesci[NrCzesciP].Czesc[Czesci[NrCzesciP].High].TotalNotes));
  //Total Notes Patch End


  // nowa czesc //Neuer Satz //Update New Part
  SetLength(Czesci[NrCzesciP].Czesc, Czesci[NrCzesciP].Ilosc + 1);
  Czesci[NrCzesciP].High := Czesci[NrCzesciP].High + 1;
  Czesci[NrCzesciP].Ilosc := Czesci[NrCzesciP].Ilosc + 1;
  Czesci[NrCzesciP].Czesc[Czesci[NrCzesciP].High].HighNut := -1;

  if not AktSong.Relative then
    Czesci[NrCzesciP].Czesc[Czesci[NrCzesciP].High].Start := Param1;

  if AktSong.Relative then begin
    Czesci[NrCzesciP].Czesc[Czesci[NrCzesciP].High].Start := Param1;
    Rel[NrCzesciP] := Rel[NrCzesciP] + Param2;
  end;

  Base[NrCzesciP] := 100; // high number
end;

function WczytajCzesci(Name: string): boolean;
var
  TempC:    char;
  Tekst:    string;
  CP:       integer; // Current Player (0 or 1)
  Pet:      integer;
  Both:     boolean;
  Param1:   integer;
  Param2:   integer;
  Param3:   integer;
  ParamS:   string;
  I: Integer;
begin
  Result := false;

  if not FileExists(Name) then begin
    Log.LogError('File not found: "' + Name + '"', 'WczytajCzesci');
    exit;
  end;

  try
  MultBPM := 4; // 4 - mnoznik dla czasu nut
  Mult := 1; // 4 - dokladnosc pomiaru nut
  Base[0] := 100; // high number
//  Base[1] := 100; // high number
  Czesci[0].Wartosc := 0;
//  Czesci[1].Wartosc := 0; // here was the error in 0.3.2
  AktSong.Relative := false;

  Rel[0] := 0;
//  Rel[1] := 0;
  CP := 0;
  Both := false;
  if Length(Player) = 2 then Both := true;

  FileMode := fmOpenRead;
  AssignFile(Plik, Name);
  Reset(Plik);

  ReadHeader(AktSong);
(*  if AktSong.Title = 'Hubba Hubba Zoot Zoot' then begin
    Mult := 2;
    AktSong.BPM[0].BPM := AktSong.BPM[0].BPM * 2;
  end;*)

  SetLength(Czesci, 2);
  for Pet := 0 to High(Czesci) do begin
    SetLength(Czesci[Pet].Czesc, 1);
    Czesci[Pet].High := 0;
    Czesci[Pet].Ilosc := 1;
    Czesci[Pet].Akt := 0;
    Czesci[Pet].Resolution := AktSong.Resolution;
    Czesci[Pet].NotesGAP := AktSong.NotesGAP;
    Czesci[Pet].Czesc[0].IlNut := 0;
    Czesci[Pet].Czesc[0].HighNut := -1;
  end;

//  TempC := ':';
  TempC := PlikC; // read from backup variable, don't use default ':' value

  while (TempC <> 'E') do begin
    Inc(LineNo);
    if (TempC = ':') or (TempC = '*') or (TempC = 'F') then begin
      // wczytuje nute
      Read(Plik, Param1);
      Read(Plik, Param2);
      Read(Plik, Param3);
      Read(Plik, ParamS);

      // dodaje nute
      if not Both then
        // P1
        DodajNute(0, TempC, (Param1+Rel[0]) * Mult, Param2 * Mult, Param3, ParamS)
      else begin
        // P1 + P2
        DodajNute(0, TempC, (Param1+Rel[0]) * Mult, Param2 * Mult, Param3, ParamS);
        DodajNute(1, TempC, (Param1+Rel[1]) * Mult, Param2 * Mult, Param3, ParamS);
      end;
    end; // if
    if TempC = '-' then begin
      // reads sentence
      Read(Plik, Param1);
      if AktSong.Relative then Read(Plik, Param2); // read one more data for relative system

      // new sentence
      if not Both then
        // P1
        NewSentence(0, (Param1 + Rel[0]) * Mult, Param2)
      else begin
        // P1 + P2
        NewSentence(0, (Param1 + Rel[0]) * Mult, Param2);
        NewSentence(1, (Param1 + Rel[1]) * Mult, Param2);
      end;

    end; // if

    if TempC = 'B' then begin
      SetLength(AktSong.BPM, Length(AktSong.BPM) + 1);
      Read(Plik, AktSong.BPM[High(AktSong.BPM)].StartBeat);
      AktSong.BPM[High(AktSong.BPM)].StartBeat := AktSong.BPM[High(AktSong.BPM)].StartBeat + Rel[0];

      Read(Plik, Tekst);
      AktSong.BPM[High(AktSong.BPM)].BPM := StrToFloat(Tekst);
      AktSong.BPM[High(AktSong.BPM)].BPM := AktSong.BPM[High(AktSong.BPM)].BPM * Mult * MultBPM;
    end;


    if not Both then begin
      Czesci[CP].Czesc[Czesci[CP].High].BaseNote := Base[CP];
      Czesci[CP].Czesc[Czesci[CP].High].LyricWidth := glTextWidth(PChar(Czesci[CP].Czesc[Czesci[CP].High].Lyric));
      //Total Notes Patch
      Czesci[CP].Czesc[Czesci[CP].High].TotalNotes := 0;
      for I := low(Czesci[CP].Czesc[Czesci[CP].High].Nuta) to high(Czesci[CP].Czesc[Czesci[CP].High].Nuta) do
      begin
       Czesci[CP].Czesc[Czesci[CP].High].TotalNotes := Czesci[CP].Czesc[Czesci[CP].High].TotalNotes + Czesci[CP].Czesc[Czesci[CP].High].Nuta[I].Dlugosc * Czesci[CP].Czesc[Czesci[CP].High].Nuta[I].Wartosc;
      end;
      //Total Notes Patch End
    end else begin
      for Pet := 0 to High(Czesci) do begin
        Czesci[Pet].Czesc[Czesci[Pet].High].BaseNote := Base[Pet];
        Czesci[Pet].Czesc[Czesci[Pet].High].LyricWidth := glTextWidth(PChar(Czesci[Pet].Czesc[Czesci[Pet].High].Lyric));
        //Total Notes Patch
        Czesci[Pet].Czesc[Czesci[Pet].High].TotalNotes := 0;
        for I := low(Czesci[Pet].Czesc[Czesci[Pet].High].Nuta) to high(Czesci[Pet].Czesc[Czesci[Pet].High].Nuta) do
        begin
          Czesci[Pet].Czesc[Czesci[Pet].High].TotalNotes := Czesci[Pet].Czesc[Czesci[Pet].High].TotalNotes + Czesci[Pet].Czesc[Czesci[Pet].High].Nuta[I].Dlugosc * Czesci[Pet].Czesc[Czesci[Pet].High].Nuta[I].Wartosc;
        end;
        //Total Notes Patch End
      end;
    end;

    Read(Plik, TempC);
  end; // while}

  CloseFile(Plik);
  except
    Log.LogError('Error Loading File: "' + Name + '" in Line ' + inttostr(LineNo));
    exit;
  end;

  Result := true;
end;

function SaveSong(Song: TSong; Czesc: TCzesci; Name: string; Relative: boolean): boolean;
var
  C:      integer;
  N:      integer;
  S:      string;
  B:      integer;
  RelativeSubTime:    integer;
  NoteState: String;

begin
//  Relative := true; // override (idea - use shift+S to save with relative)
  AssignFile(Plik, Name);
  Rewrite(Plik);

  WriteLn(Plik, '#TITLE:' + Song.Title + '');
  WriteLn(Plik, '#ARTIST:' + Song.Artist);

  if Song.Creator     <> '' then    WriteLn(Plik, '#CREATOR:'     + Song.Creator);
  if Song.Edition     <> 'Unknown' then WriteLn(Plik, '#EDITION:' + Song.Edition);
  if Song.Genre       <> 'Unknown' then   WriteLn(Plik, '#GENRE:' + Song.Genre);
  if Song.Language    <> 'Unknown' then    WriteLn(Plik, '#LANGUAGE:'    + Song.Language);
  if Song.Cover       <> '' then    WriteLn(Plik, '#COVER:'       + Song.Cover);

  WriteLn(Plik, '#MP3:' + Song.Mp3);

  if Song.Background  <> '' then    WriteLn(Plik, '#BACKGROUND:'  + Song.Background);
  if Song.Video       <> '' then    WriteLn(Plik, '#VIDEO:'       + Song.Video);
  if Song.VideoGAP    <> 0  then    WriteLn(Plik, '#VIDEOGAP:'    + FloatToStr(Song.VideoGAP));
  if Song.Resolution  <> 4  then    WriteLn(Plik, '#RESOLUTION:'  + IntToStr(Song.Resolution));
  if Song.NotesGAP    <> 0  then    WriteLn(Plik, '#NOTESGAP:'    + IntToStr(Song.NotesGAP));
  if Song.Start       <> 0  then    WriteLn(Plik, '#START:'       + FloatToStr(Song.Start));
  if Song.Finish      <> 0  then    WriteLn(Plik, '#END:'         + IntToStr(Song.Finish));
  if Relative               then    WriteLn(Plik, '#RELATIVE:yes');

  WriteLn(Plik, '#BPM:' + FloatToStr(Song.BPM[0].BPM / 4));
  WriteLn(Plik, '#GAP:' + FloatToStr(Song.GAP));

  RelativeSubTime := 0;
  for B := 1 to High(AktSong.BPM) do
    WriteLn(Plik, 'B ' + FloatToStr(AktSong.BPM[B].StartBeat) + ' ' + FloatToStr(AktSong.BPM[B].BPM/4));

  for C := 0 to Czesc.High do begin
    for N := 0 to Czesc.Czesc[C].HighNut do begin
      with Czesc.Czesc[C].Nuta[N] do begin


        //Golden + Freestyle Note Patch
        case Czesc.Czesc[C].Nuta[N].Wartosc of
          0: NoteState := 'F ';
          1: NoteState := ': ';
          2: NoteState := '* ';
        end; // case
        S := NoteState + IntToStr(Start-RelativeSubTime) + ' ' + IntToStr(Dlugosc) + ' ' + IntToStr(Ton) + ' ' + Tekst;


        WriteLn(Plik, S);
      end; // with
    end; // N

    if C < Czesc.High then begin      // don't write end of last sentence
      if not Relative then
        S := '- ' + IntToStr(Czesc.Czesc[C+1].Start)
      else begin
        S := '- ' + IntToStr(Czesc.Czesc[C+1].Start - RelativeSubTime) +
          ' ' + IntToStr(Czesc.Czesc[C+1].Start - RelativeSubTime);
        RelativeSubTime := Czesc.Czesc[C+1].Start;
      end;
      WriteLn(Plik, S);
    end;

  end; // C


  WriteLn(Plik, 'E');
  CloseFile(Plik);
end;

function SaveSongDebug(Song: TSong; Czesc: TCzesci; Name: string; Relative: boolean): boolean;
var
  C:      integer;
  N:      integer;
  S:      string;
  STon:   integer;
  SLen:   integer;
  NTot:   integer;
  PlikB:  TextFile;
  LastTime:   integer;
begin
  AssignFile(Plik, Name);
  Rewrite(Plik);

  AssignFile(PlikB, 'C:\song db.asm');
  Rewrite(PlikB);

  NTot := 0;
  LastTime := 0;

  for C := 0 to Czesc.High do begin
    WriteLn(Plik, '; ' + IntToStr(C));

    for N := 0 to Czesc.Czesc[C].HighNut do begin
      with Czesc.Czesc[C].Nuta[N] do begin

        // timespace
        if LastTime < Start then begin
          STon := 0;
          SLen := Round((Start - LastTime) * 16320 / 255 / 12);
          WriteLn(PlikB, '    .dw ' + IntToStr(STon + SLen*256) + ' ; timespace (0, ' + IntToStr(SLen) + ')');

        end;



        // ton
        STon := Round(98940/(2*261.62*Power(1.05946309436, Ton)));
        S := '    ldi R18, ' + IntToStr(STon);
        if STon > 255 then begin
          beep;
          S := '!!!!' + S;
        end;
        WriteLn(Plik, S);

        // length
	      //ldi R19, 43
        SLen := Round(Dlugosc * 16320 / STon / 12);
        S := '    ldi R19, ' + IntToStr(SLen);
        if SLen > 255 then begin
          beep;
          S := '!!!!' + S;
        end;
        WriteLn(Plik, S);

        // function
        S := '    rcall playtone';
        WriteLn(Plik, S);

        // song dw
        WriteLn(PlikB, '    .dw ' + IntToStr(STon + SLen*256));


        LastTime := Start + Dlugosc;
        Inc(NTot);

      end; // with
    end; // N
    WriteLn(Plik, '');
    WriteLn(PlikB, '');
  end; // C

  WriteLn(Plik, '; nut ' + IntToStr(NTot));
  WriteLn(Plik, '; bajt�w ' + IntToStr(8*NTot));

  WriteLn(PlikB, '    .dw 0');
  WriteLn(PlikB, '; nut ' + IntToStr(NTot));
  WriteLn(PlikB, '; bajt�w ' + IntToStr(2*NTot));


  CloseFile(Plik);
  CloseFile(PlikB);
end;

end.