{* 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);
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.