aboutsummaryrefslogtreecommitdiffstats
path: root/Game/Code/Classes/USong_Txt.pas
diff options
context:
space:
mode:
Diffstat (limited to 'Game/Code/Classes/USong_Txt.pas')
-rw-r--r--Game/Code/Classes/USong_Txt.pas438
1 files changed, 438 insertions, 0 deletions
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