aboutsummaryrefslogblamecommitdiffstats
path: root/songmanagement/src/base/UFiles.pas
blob: 1a7ca8f8af194e13817ae9f05dbed9e319a16068 (plain) (tree)





















































































































































































































                                                                                                                 
{* UltraStar Deluxe - Karaoke Game
 *
 * UltraStar Deluxe is the legal property of its developers, whose names
 * are too numerous to list here. Please refer to the COPYRIGHT
 * file distributed with this source distribution.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; see the file COPYING. If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 *
 * $URL$
 * $Id$
 *}

unit UFiles;

interface

{$IFDEF FPC}
  {$MODE Delphi}
{$ENDIF}
{$I switches.inc}

uses
  SysUtils,
  Classes,
  ULog,
  UMusic,
  USongs,
  USong,
  UPath;

procedure ResetSingTemp;

type
  TSaveSongResult = (ssrOK, ssrFileError, ssrEncodingError);

{**
 * Throws a TEncodingException if the song's fields cannot be encoded in the
 * requested encoding.
 *}
function SaveSong(const Song: TSong; const Lines: TLines; const Name: IPath; Relative: boolean): TSaveSongResult;

implementation

uses
  TextGL,
  UIni,
  UNote,
  UPlatform,
  UUnicodeUtils,
  UTextEncoding;

//--------------------
// Resets the temporary Sentence Arrays for each Player and some other Variables
//--------------------
procedure ResetSingTemp;
var
  Count:  integer;
begin
  SetLength(Lines, Length(Player));
  for Count := 0 to High(Player) do begin
    SetLength(Lines[Count].Line, 1);
    SetLength(Lines[Count].Line[0].Note, 0);
    Lines[Count].Line[0].Lyric := '';
    Player[Count].Score := 0;
    Player[Count].LengthNote := 0;
    Player[Count].HighNote := -1;
  end;
end;

//--------------------
// Saves a Song
//--------------------
function SaveSong(const Song: TSong; const Lines: TLines; const Name: IPath; Relative: boolean): TSaveSongResult;
var
  C:      integer;
  N:      integer;
  S:      AnsiString;
  B:      integer;
  RelativeSubTime: integer;
  NoteState: AnsiString;
  SongFile: TTextFileStream;

  function EncodeToken(const Str: UTF8String): RawByteString;
  var
    Success: boolean;
  begin
    Success := EncodeStringUTF8(Str, Result, Song.Encoding);
    if (not Success) then
      SaveSong := ssrEncodingError;
  end;

  procedure WriteCustomTags;
    var
      I: integer;
      Line: RawByteString;
  begin
    for I := 0 to High(Song.CustomTags) do
    begin
      Line := EncodeToken(Song.CustomTags[I].Content);
      if (Length(Song.CustomTags[I].Tag) > 0) then
        Line := EncodeToken(Song.CustomTags[I].Tag) + ':' + Line;

      SongFile.WriteLine('#' + Line);
    end;

  end;

begin
  //  Relative := true; // override (idea - use shift+S to save with relative)
  Result := ssrOK;

  try
    SongFile := TMemTextFileStream.Create(Name, fmCreate);
    try
      // to-do: should we really write the BOM?
      //        it causes problems w/ older versions
      //        e.g. usdx 1.0.1a or ultrastar < 0.7.0
      if (Song.Encoding = encUTF8) then
        SongFile.WriteString(UTF8_BOM);

      // do not save "auto" encoding tag
      if (Song.Encoding <> encAuto) then
        SongFile.WriteLine('#ENCODING:' + EncodingName(Song.Encoding));
      SongFile.WriteLine('#TITLE:'    + EncodeToken(Song.Title));
      SongFile.WriteLine('#ARTIST:'   + EncodeToken(Song.Artist));

      if Song.Creator     <> ''        then SongFile.WriteLine('#CREATOR:'   + EncodeToken(Song.Creator));
      if Song.Edition     <> 'Unknown' then SongFile.WriteLine('#EDITION:'   + EncodeToken(Song.Edition));
      if Song.Genre       <> 'Unknown' then SongFile.WriteLine('#GENRE:'     + EncodeToken(Song.Genre));
      if Song.Language    <> 'Unknown' then SongFile.WriteLine('#LANGUAGE:'  + EncodeToken(Song.Language));
      if Song.Year        <> 0         then SongFile.WriteLine('#YEAR:'      + IntToStr(Song.Year));

      SongFile.WriteLine('#MP3:' + EncodeToken(Song.Mp3.ToUTF8));
      if Song.Cover.IsSet      then    SongFile.WriteLine('#COVER:'       + EncodeToken(Song.Cover.ToUTF8));
      if Song.Background.IsSet then    SongFile.WriteLine('#BACKGROUND:'  + EncodeToken(Song.Background.ToUTF8));
      if Song.Video.IsSet      then    SongFile.WriteLine('#VIDEO:'       + EncodeToken(Song.Video.ToUTF8));

      if Song.VideoGAP    <> 0  then    SongFile.WriteLine('#VIDEOGAP:'    + FloatToStr(Song.VideoGAP));
      if Song.Resolution  <> 4  then    SongFile.WriteLine('#RESOLUTION:'  + IntToStr(Song.Resolution));
      if Song.NotesGAP    <> 0  then    SongFile.WriteLine('#NOTESGAP:'    + IntToStr(Song.NotesGAP));
      if Song.Start       <> 0  then    SongFile.WriteLine('#START:'       + FloatToStr(Song.Start));
      if Song.Finish      <> 0  then    SongFile.WriteLine('#END:'         + IntToStr(Song.Finish));
      if Relative               then    SongFile.WriteLine('#RELATIVE:yes');

      SongFile.WriteLine('#BPM:' + FloatToStr(Song.BPM[0].BPM / 4));
      SongFile.WriteLine('#GAP:' + FloatToStr(Song.GAP));

      // write custom header tags
      WriteCustomTags;

      RelativeSubTime := 0;
      for B := 1 to High(Song.BPM) do
        SongFile.WriteLine('B ' + FloatToStr(Song.BPM[B].StartBeat) + ' '
                                + FloatToStr(Song.BPM[B].BPM/4));

      for C := 0 to Lines.High do
      begin
        for N := 0 to Lines.Line[C].HighNote do
        begin
          with Lines.Line[C].Note[N] do
          begin
            //Golden + Freestyle Note Patch
            case Lines.Line[C].Note[N].NoteType of
              ntFreestyle: NoteState := 'F ';
              ntNormal: NoteState := ': ';
              ntGolden: NoteState := '* ';
            end; // case
            S := NoteState + IntToStr(Start-RelativeSubTime) + ' '
                           + IntToStr(Length) + ' '
                           + IntToStr(Tone) + ' '
                           + EncodeToken(Text);

            SongFile.WriteLine(S);
          end; // with
        end; // N

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

      SongFile.WriteLine('E');
    finally
      SongFile.Free;
    end;
  except
    Result := ssrFileError;
  end;
end;

end.