aboutsummaryrefslogtreecommitdiffstats
path: root/Game/Code/Classes/UXMLSong.pas
diff options
context:
space:
mode:
Diffstat (limited to 'Game/Code/Classes/UXMLSong.pas')
-rw-r--r--Game/Code/Classes/UXMLSong.pas1146
1 files changed, 573 insertions, 573 deletions
diff --git a/Game/Code/Classes/UXMLSong.pas b/Game/Code/Classes/UXMLSong.pas
index ddcb173a..1a1fe6bc 100644
--- a/Game/Code/Classes/UXMLSong.pas
+++ b/Game/Code/Classes/UXMLSong.pas
@@ -1,573 +1,573 @@
-unit UXMLSong;
-
-interface
-uses Classes;
-
-type
- TNote = record
- Start: Cardinal;
- Duration: Cardinal;
- Tone: Integer;
- NoteTyp: Byte;
- Lyric: String;
- end;
- ANote = Array of TNote;
-
- TSentence = record
- Singer: Byte;
- Duration: Cardinal;
- Notes: ANote;
- end;
- ASentence = Array of TSentence;
-
- TSongInfo = Record
- ID: Cardinal;
- DualChannel: Boolean;
- Header: Record
- Artist: String;
- Title: String;
- Gap: Cardinal;
- BPM: Real;
- Resolution: Byte;
- Edition: String;
- Genre: String;
- Year: String;
- Language: String;
- end;
- CountSentences: Cardinal;
- Sentences: ASentence;
- end;
-
- TParser = class
- private
- SSFile: TStringList;
-
- ParserState: Byte;
- CurPosinSong: Cardinal; //Cur Beat Pos in the Song
- CurDuettSinger: Byte; //Who sings this Part?
- BindLyrics: Boolean; //Should the Lyrics be bind to the last Word (no Space)
- FirstNote: Boolean; //Is this the First Note found? For Gap calculating
-
- Function ParseLine(Line: String): Boolean;
- public
- SongInfo: TSongInfo;
- ErrorMessage: String;
- Edition: String;
- SingstarVersion: String;
-
- Settings: Record
- DashReplacement: Char;
- end;
-
- Constructor Create;
-
- Function ParseConfigforEdition(const Filename: String): String;
-
- Function ParseSongHeader(const Filename: String): Boolean; //Parse Song Header only
- Function ParseSong (const Filename: String): Boolean; //Parse whole Song
- end;
-
-const
- PS_None = 0;
- PS_Melody = 1;
- PS_Sentence = 2;
-
- NT_Normal = 1;
- NT_Freestyle = 0;
- NT_Golden = 2;
-
- DS_Player1 = 1;
- DS_Player2 = 2;
- DS_Both = 3;
-
-implementation
-uses SysUtils, StrUtils;
-
-Constructor TParser.Create;
-begin
- inherited Create;
- ErrorMessage := '';
-
- DecimalSeparator := '.';
-end;
-
-Function TParser.ParseSong (const Filename: String): Boolean;
-var I: Integer;
-begin
- Result := False;
- if FileExists(Filename) then
- begin
- SSFile := TStringList.Create;
-
- try
- ErrorMessage := 'Can''t open melody.xml file';
- SSFile.LoadFromFile(Filename);
- ErrorMessage := '';
- Result := True;
- I := 0;
-
- SongInfo.CountSentences := 0;
- CurDuettSinger := DS_Both; //Both is Singstar Standard
- CurPosinSong := 0; //Start at Pos 0
- BindLyrics := True; //Dont start with Space
- FirstNote := True; //First Note found should be the First Note ;)
-
- SongInfo.Header.Language := '';
- SongInfo.Header.Edition := Edition;
- SongInfo.DualChannel := False;
-
- ParserState := PS_None;
-
- SetLength(SongInfo.Sentences, 0);
-
- While Result And (I < SSFile.Count) do
- begin
- Result := ParseLine(SSFile.Strings[I]);
-
- Inc(I);
- end;
-
- finally
- SSFile.Free;
- end;
- end;
-end;
-
-Function TParser.ParseSongHeader (const Filename: String): Boolean;
-var I: Integer;
-begin
- Result := False;
- if FileExists(Filename) then
- begin
- SSFile := TStringList.Create;
- SSFile.Clear;
-
- try
- SSFile.LoadFromFile(Filename);
-
- If (SSFile.Count > 0) then
- begin
- Result := True;
- I := 0;
-
- SongInfo.CountSentences := 0;
- CurDuettSinger := DS_Both; //Both is Singstar Standard
- CurPosinSong := 0; //Start at Pos 0
- BindLyrics := True; //Dont start with Space
- FirstNote := True; //First Note found should be the First Note ;)
-
- SongInfo.ID := 0;
- SongInfo.Header.Language := '';
- SongInfo.Header.Edition := Edition;
- SongInfo.DualChannel := False;
- ParserState := PS_None;
-
- While (SongInfo.ID < 4) AND Result And (I < SSFile.Count) do
- begin
- Result := ParseLine(SSFile.Strings[I]);
-
- Inc(I);
- end;
- end
- else
- ErrorMessage := 'Can''t open melody.xml file';
-
- finally
- SSFile.Free;
- end;
- end
- else
- ErrorMessage := 'Can''t find melody.xml file';
-end;
-
-Function TParser.ParseLine(Line: String): Boolean;
-var
- Tag: String;
- Values: String;
- AValues: Array of Record
- Name: String;
- Value: String;
- end;
- I, J, K: Integer;
- Duration, Tone: Integer;
- Lyric: String;
- NoteType: Byte;
-
- Procedure MakeValuesArray;
- var Len, Pos, State, StateChange: Integer;
- begin
- Len := -1;
- SetLength(AValues, Len + 1);
-
- Pos := 1;
- State := 0;
- While (Pos <= Length(Values)) AND (Pos <> 0) do
- begin
- Case State of
-
- 0: begin //Search for ValueName
- If (Values[Pos] <> ' ') AND (Values[Pos] <> '=') then
- begin
- //Found Something
- State := 1; //State search for '='
- StateChange := Pos; //Save Pos of Change
- Pos := PosEx('=', Values, Pos + 1);
- end
- else Inc(Pos); //When nothing found then go to next char
- end;
-
- 1: begin //Search for Equal Mark
- //Add New Value
- Inc(Len);
- SetLength(AValues, Len + 1);
-
- AValues[Len].Name := UpperCase(Copy(Values, StateChange, Pos - StateChange));
-
-
- State := 2; //Now Search for starting '"'
- StateChange := Pos; //Save Pos of Change
- Pos := PosEx('"', Values, Pos + 1);
- end;
-
- 2: begin //Search for starting '"' or ' ' <- End if there was no "
- If (Values[Pos] = '"') then
- begin //Found starting '"'
- State := 3; //Now Search for ending '"'
- StateChange := Pos; //Save Pos of Change
- Pos := PosEx('"', Values, Pos + 1);
- end
- else If (Values[Pos] = ' ') then //Found ending Space
- begin
- //Save Value to Array
- AValues[Len].Value := Copy(Values, StateChange + 1, Pos - StateChange - 1);
-
- //Search for next Valuename
- State := 0;
- StateChange := Pos;
- Inc(Pos);
- end;
- end;
-
- 3: begin //Search for ending '"'
- //Save Value to Array
- AValues[Len].Value := Copy(Values, StateChange + 1, Pos - StateChange - 1);
-
- //Search for next Valuename
- State := 0;
- StateChange := Pos;
- Inc(Pos);
- end;
- end;
-
- If (State >= 2) then
- begin //Save Last Value
- AValues[Len].Value := Copy(Values, StateChange + 1, Length(Values) - StateChange);
- end;
- end;
- end;
-begin
- Result := True;
-
- Line := Trim(Line);
- If (Length(Line) > 0) then
- begin
- I := Pos('<', Line);
- J := PosEx(' ', Line, I+1);
- K := PosEx('>', Line, I+1);
-
- If (J = 0) then J := K
- Else If (K < J) AND (K <> 0) then J := K; //Use nearest Tagname End indicator
- Tag := UpperCase(copy(Line, I + 1, J - I - 1));
- Values := copy(Line, J + 1, K - J - 1);
-
- Case ParserState of
- PS_None: begin//Search for Melody Tag
- If (Tag = 'MELODY') then
- begin
- Inc(SongInfo.ID); //Inc SongID when header Information is added
- MakeValuesArray;
- For I := 0 to High(AValues) do
- begin
- If (AValues[I].Name = 'TEMPO') then
- begin
- SongInfo.Header.BPM := StrtoFloatDef(AValues[I].Value, 0);
- If (SongInfo.Header.BPM <= 0) then
- begin
- Result := False;
- ErrorMessage := 'Can''t read BPM from Song';
- end;
- end
-
- Else If (AValues[I].Name = 'RESOLUTION') then
- begin
- AValues[I].Value := Uppercase(AValues[I].Value);
- //Ultrastar Resolution is "how often a Beat is split / 4"
- If (AValues[I].Value = 'HEMIDEMISEMIQUAVER') then
- SongInfo.Header.Resolution := 64 div 4
- Else If (AValues[I].Value = 'DEMISEMIQUAVER') then
- SongInfo.Header.Resolution := 32 div 4
- Else If (AValues[I].Value = 'SEMIQUAVER') then
- SongInfo.Header.Resolution := 16 div 4
- Else If (AValues[I].Value = 'QUAVER') then
- SongInfo.Header.Resolution := 8 div 4
- Else If (AValues[I].Value = 'CROTCHET') then
- SongInfo.Header.Resolution := 4 div 4
- Else
- begin //Can't understand teh Resolution :/
- Result := False;
- ErrorMessage := 'Can''t read Resolution from Song';
- end;
- end
-
- Else If (AValues[I].Name = 'GENRE') then
- begin
- SongInfo.Header.Genre := AValues[I].Value;
- end
-
- Else If (AValues[I].Name = 'YEAR') then
- begin
- SongInfo.Header.Year := AValues[I].Value;
- end
-
- Else If (AValues[I].Name = 'VERSION') then
- begin
- SingstarVersion := AValues[I].Value;
- end;
- end;
-
- ParserState := PS_Melody; //In Melody Tag
- end;
- end;
-
-
- PS_Melody: begin //Search for Sentence, Artist/Title Info or eo Melody
- If (Tag = 'SENTENCE') then
- begin
- ParserState := PS_Sentence; //Parse in a Sentence Tag now
-
- //Increase SentenceCount
- Inc(SongInfo.CountSentences);
-
- BindLyrics := True; //Don't let Txts Begin w/ Space
-
- //Search for Duett Singer Info
- MakeValuesArray;
- For I := 0 to High(AValues) do
- If (AValues[I].Name = 'SINGER') then
- begin
- AValues[I].Value := Uppercase(AValues[I].Value);
- If (AValues[I].Value = 'SOLO 1') then
- CurDuettSinger := DS_Player1
- Else If (AValues[I].Value = 'SOLO 2') then
- CurDuettSinger := DS_Player2
- Else
- CurDuettSinger := DS_Both; //In case of "Group" or anything that is not identified use Both
- end;
- end
-
- Else If (Tag = '!--') then
- begin //Comment, this may be Artist or Title Info
- I := Pos(':', Values); //Search for Delimiter
-
- If (I <> 0) then //If Found check for Title or Artist
- begin
- //Copy Title or Artist Tag to Tag String
- Tag := Uppercase(Trim(Copy(Values, 1, I - 1)));
-
- If (Tag = 'ARTIST') then
- begin
- SongInfo.Header.Artist := Trim(Copy(Values, I + 1, Length(Values) - I - 2));
- Inc(SongInfo.ID); //Inc SongID when header Information is added
- end
- Else If (Tag = 'TITLE') then
- begin
- SongInfo.Header.Title := Trim(Copy(Values, I + 1, Length(Values) - I - 2));
- Inc(SongInfo.ID); //Inc SongID when header Information is added
- end;
- end;
- end
-
- //Parsing for weird "Die toten Hosen" Tags
- Else If (Tag = '!--ARTIST:') OR (Tag = '!--ARTIST') then
- begin //Comment, with Artist Info
- I := Pos(':', Values); //Search for Delimiter
-
- Inc(SongInfo.ID); //Inc SongID when header Information is added
-
- SongInfo.Header.Artist := Trim(Copy(Values, I + 1, Length(Values) - I - 2));
- end
-
- Else If (Tag = '!--TITLE:') OR (Tag = '!--TITLE') then
- begin //Comment, with Artist Info
- I := Pos(':', Values); //Search for Delimiter
-
- Inc(SongInfo.ID); //Inc SongID when header Information is added
-
- SongInfo.Header.Title := Trim(Copy(Values, I + 1, Length(Values) - I - 2));
- end
-
- Else If (Tag = '/MELODY') then
- begin
- ParserState := PS_None;
- Exit; //Stop Parsing, Melody iTag ended
- end
- end;
-
-
- PS_Sentence: begin //Search for Notes or eo Sentence
- If (Tag = 'NOTE') then
- begin //Found Note
- //Get Values
- MakeValuesArray;
-
- NoteType := NT_Normal;
- For I := 0 to High(AValues) do
- begin
- If (AValues[I].Name = 'DURATION') then
- begin
- Duration := StrtoIntDef(AValues[I].Value, -1);
- If (Duration < 0) then
- begin
- Result := False;
- ErrorMessage := 'Can''t read duration from Note in Line: "' + Line + '"';
- Exit;
- end;
- end
- Else If (AValues[I].Name = 'MIDINOTE') then
- begin
- Tone := StrtoIntDef(AValues[I].Value, 0);
- end
- Else If (AValues[I].Name = 'BONUS') AND (Uppercase(AValues[I].Value) = 'YES') then
- begin
- NoteType := NT_Golden;
- end
- Else If (AValues[I].Name = 'FREESTYLE') AND (Uppercase(AValues[I].Value) = 'YES') then
- begin
- NoteType := NT_Freestyle;
- end
- Else If (AValues[I].Name = 'LYRIC') then
- begin
- Lyric := AValues[I].Value;
-
- If (Length(Lyric) > 0) then
- begin
- If (Lyric = '-') then
- Lyric[1] := Settings.DashReplacement;
-
- If (not BindLyrics) then
- Lyric := ' ' + Lyric;
-
-
- If (Length(Lyric) > 2) AND (Lyric[Length(Lyric)-1] = ' ') AND (Lyric[Length(Lyric)] = '-') then
- begin //Between this and the next Lyric should be no space
- BindLyrics := True;
- SetLength(Lyric, Length(Lyric) - 2);
- end
- else
- BindLyrics := False; //There should be a Space
- end;
- end;
- end;
-
- //Add Note
- I := SongInfo.CountSentences - 1;
-
- If (Length(Lyric) > 0) then
- begin //Real note, no rest
- //First Note of Sentence
- If (Length(SongInfo.Sentences) < SongInfo.CountSentences) then
- begin
- SetLength(SongInfo.Sentences, SongInfo.CountSentences);
- SetLength(SongInfo.Sentences[I].Notes, 0);
- end;
-
- //First Note of Song -> Generate Gap
- If (FirstNote) then
- begin
- //Calculate Gap
- If (SongInfo.Header.Resolution <> 0) AND (SongInfo.Header.BPM <> 0) then
- SongInfo.Header.Gap := Round(CurPosinSong / (SongInfo.Header.BPM*SongInfo.Header.Resolution) * 60000)
- Else
- begin
- Result := False;
- ErrorMessage := 'Can''t calculate Gap, no Resolution or BPM present.';
- Exit;
- end;
-
- CurPosinSong := 0; //Start at 0, because Gap goes until here
- Inc(SongInfo.ID); //Add Header Value therefore Inc
- FirstNote := False;
- end;
-
- J := Length(SongInfo.Sentences[I].Notes);
- SetLength(SongInfo.Sentences[I].Notes, J + 1);
- SongInfo.Sentences[I].Notes[J].Start := CurPosinSong;
- SongInfo.Sentences[I].Notes[J].Duration := Duration;
- SongInfo.Sentences[I].Notes[J].Tone := Tone;
- SongInfo.Sentences[I].Notes[J].NoteTyp := NoteType;
- SongInfo.Sentences[I].Notes[J].Lyric := Lyric;
-
- //Inc Pos in Song
- Inc(CurPosInSong, Duration);
- end
- else
- begin
- //just change pos in Song
- Inc(CurPosInSong, Duration);
- end;
-
-
- end
- Else If (Tag = '/SENTENCE') then
- begin //End of Sentence Tag
- ParserState := PS_Melody;
-
- //Delete Sentence if no Note is Added
- If (Length(SongInfo.Sentences) <> SongInfo.CountSentences) then
- begin
- SongInfo.CountSentences := Length(SongInfo.Sentences);
- end;
- end;
- end;
- end;
-
- end
- else //Empty Line -> parsed succesful ;)
- Result := true;
-end;
-
-Function TParser.ParseConfigforEdition(const Filename: String): String;
-var
- txt: TStringlist;
- I: Integer;
- J, K: Integer;
- S: String;
-begin
- Result := '';
- txt := TStringlist.Create;
- try
- txt.LoadFromFile(Filename);
-
- For I := 0 to txt.Count-1 do
- begin
- S := Trim(txt.Strings[I]);
- J := Pos('<PRODUCT_NAME>', S);
-
- If (J <> 0) then
- begin
- Inc(J, 14);
- K := Pos('</PRODUCT_NAME>', S);
- If (K<J) then K := Length(S) + 1;
-
- Result := Copy(S, J, K - J);
- Break;
- end;
- end;
-
- Edition := Result;
- finally
- txt.Free;
- end;
-end;
-
-end.
+unit UXMLSong;
+
+interface
+uses Classes;
+
+type
+ TNote = record
+ Start: Cardinal;
+ Duration: Cardinal;
+ Tone: Integer;
+ NoteTyp: Byte;
+ Lyric: String;
+ end;
+ ANote = Array of TNote;
+
+ TSentence = record
+ Singer: Byte;
+ Duration: Cardinal;
+ Notes: ANote;
+ end;
+ ASentence = Array of TSentence;
+
+ TSongInfo = Record
+ ID: Cardinal;
+ DualChannel: Boolean;
+ Header: Record
+ Artist: String;
+ Title: String;
+ Gap: Cardinal;
+ BPM: Real;
+ Resolution: Byte;
+ Edition: String;
+ Genre: String;
+ Year: String;
+ Language: String;
+ end;
+ CountSentences: Cardinal;
+ Sentences: ASentence;
+ end;
+
+ TParser = class
+ private
+ SSFile: TStringList;
+
+ ParserState: Byte;
+ CurPosinSong: Cardinal; //Cur Beat Pos in the Song
+ CurDuettSinger: Byte; //Who sings this Part?
+ BindLyrics: Boolean; //Should the Lyrics be bind to the last Word (no Space)
+ FirstNote: Boolean; //Is this the First Note found? For Gap calculating
+
+ Function ParseLine(Line: String): Boolean;
+ public
+ SongInfo: TSongInfo;
+ ErrorMessage: String;
+ Edition: String;
+ SingstarVersion: String;
+
+ Settings: Record
+ DashReplacement: Char;
+ end;
+
+ Constructor Create;
+
+ Function ParseConfigforEdition(const Filename: String): String;
+
+ Function ParseSongHeader(const Filename: String): Boolean; //Parse Song Header only
+ Function ParseSong (const Filename: String): Boolean; //Parse whole Song
+ end;
+
+const
+ PS_None = 0;
+ PS_Melody = 1;
+ PS_Sentence = 2;
+
+ NT_Normal = 1;
+ NT_Freestyle = 0;
+ NT_Golden = 2;
+
+ DS_Player1 = 1;
+ DS_Player2 = 2;
+ DS_Both = 3;
+
+implementation
+uses SysUtils, StrUtils;
+
+Constructor TParser.Create;
+begin
+ inherited Create;
+ ErrorMessage := '';
+
+ DecimalSeparator := '.';
+end;
+
+Function TParser.ParseSong (const Filename: String): Boolean;
+var I: Integer;
+begin
+ Result := False;
+ if FileExists(Filename) then
+ begin
+ SSFile := TStringList.Create;
+
+ try
+ ErrorMessage := 'Can''t open melody.xml file';
+ SSFile.LoadFromFile(Filename);
+ ErrorMessage := '';
+ Result := True;
+ I := 0;
+
+ SongInfo.CountSentences := 0;
+ CurDuettSinger := DS_Both; //Both is Singstar Standard
+ CurPosinSong := 0; //Start at Pos 0
+ BindLyrics := True; //Dont start with Space
+ FirstNote := True; //First Note found should be the First Note ;)
+
+ SongInfo.Header.Language := '';
+ SongInfo.Header.Edition := Edition;
+ SongInfo.DualChannel := False;
+
+ ParserState := PS_None;
+
+ SetLength(SongInfo.Sentences, 0);
+
+ While Result And (I < SSFile.Count) do
+ begin
+ Result := ParseLine(SSFile.Strings[I]);
+
+ Inc(I);
+ end;
+
+ finally
+ SSFile.Free;
+ end;
+ end;
+end;
+
+Function TParser.ParseSongHeader (const Filename: String): Boolean;
+var I: Integer;
+begin
+ Result := False;
+ if FileExists(Filename) then
+ begin
+ SSFile := TStringList.Create;
+ SSFile.Clear;
+
+ try
+ SSFile.LoadFromFile(Filename);
+
+ If (SSFile.Count > 0) then
+ begin
+ Result := True;
+ I := 0;
+
+ SongInfo.CountSentences := 0;
+ CurDuettSinger := DS_Both; //Both is Singstar Standard
+ CurPosinSong := 0; //Start at Pos 0
+ BindLyrics := True; //Dont start with Space
+ FirstNote := True; //First Note found should be the First Note ;)
+
+ SongInfo.ID := 0;
+ SongInfo.Header.Language := '';
+ SongInfo.Header.Edition := Edition;
+ SongInfo.DualChannel := False;
+ ParserState := PS_None;
+
+ While (SongInfo.ID < 4) AND Result And (I < SSFile.Count) do
+ begin
+ Result := ParseLine(SSFile.Strings[I]);
+
+ Inc(I);
+ end;
+ end
+ else
+ ErrorMessage := 'Can''t open melody.xml file';
+
+ finally
+ SSFile.Free;
+ end;
+ end
+ else
+ ErrorMessage := 'Can''t find melody.xml file';
+end;
+
+Function TParser.ParseLine(Line: String): Boolean;
+var
+ Tag: String;
+ Values: String;
+ AValues: Array of Record
+ Name: String;
+ Value: String;
+ end;
+ I, J, K: Integer;
+ Duration, Tone: Integer;
+ Lyric: String;
+ NoteType: Byte;
+
+ Procedure MakeValuesArray;
+ var Len, Pos, State, StateChange: Integer;
+ begin
+ Len := -1;
+ SetLength(AValues, Len + 1);
+
+ Pos := 1;
+ State := 0;
+ While (Pos <= Length(Values)) AND (Pos <> 0) do
+ begin
+ Case State of
+
+ 0: begin //Search for ValueName
+ If (Values[Pos] <> ' ') AND (Values[Pos] <> '=') then
+ begin
+ //Found Something
+ State := 1; //State search for '='
+ StateChange := Pos; //Save Pos of Change
+ Pos := PosEx('=', Values, Pos + 1);
+ end
+ else Inc(Pos); //When nothing found then go to next char
+ end;
+
+ 1: begin //Search for Equal Mark
+ //Add New Value
+ Inc(Len);
+ SetLength(AValues, Len + 1);
+
+ AValues[Len].Name := UpperCase(Copy(Values, StateChange, Pos - StateChange));
+
+
+ State := 2; //Now Search for starting '"'
+ StateChange := Pos; //Save Pos of Change
+ Pos := PosEx('"', Values, Pos + 1);
+ end;
+
+ 2: begin //Search for starting '"' or ' ' <- End if there was no "
+ If (Values[Pos] = '"') then
+ begin //Found starting '"'
+ State := 3; //Now Search for ending '"'
+ StateChange := Pos; //Save Pos of Change
+ Pos := PosEx('"', Values, Pos + 1);
+ end
+ else If (Values[Pos] = ' ') then //Found ending Space
+ begin
+ //Save Value to Array
+ AValues[Len].Value := Copy(Values, StateChange + 1, Pos - StateChange - 1);
+
+ //Search for next Valuename
+ State := 0;
+ StateChange := Pos;
+ Inc(Pos);
+ end;
+ end;
+
+ 3: begin //Search for ending '"'
+ //Save Value to Array
+ AValues[Len].Value := Copy(Values, StateChange + 1, Pos - StateChange - 1);
+
+ //Search for next Valuename
+ State := 0;
+ StateChange := Pos;
+ Inc(Pos);
+ end;
+ end;
+
+ If (State >= 2) then
+ begin //Save Last Value
+ AValues[Len].Value := Copy(Values, StateChange + 1, Length(Values) - StateChange);
+ end;
+ end;
+ end;
+begin
+ Result := True;
+
+ Line := Trim(Line);
+ If (Length(Line) > 0) then
+ begin
+ I := Pos('<', Line);
+ J := PosEx(' ', Line, I+1);
+ K := PosEx('>', Line, I+1);
+
+ If (J = 0) then J := K
+ Else If (K < J) AND (K <> 0) then J := K; //Use nearest Tagname End indicator
+ Tag := UpperCase(copy(Line, I + 1, J - I - 1));
+ Values := copy(Line, J + 1, K - J - 1);
+
+ Case ParserState of
+ PS_None: begin//Search for Melody Tag
+ If (Tag = 'MELODY') then
+ begin
+ Inc(SongInfo.ID); //Inc SongID when header Information is added
+ MakeValuesArray;
+ For I := 0 to High(AValues) do
+ begin
+ If (AValues[I].Name = 'TEMPO') then
+ begin
+ SongInfo.Header.BPM := StrtoFloatDef(AValues[I].Value, 0);
+ If (SongInfo.Header.BPM <= 0) then
+ begin
+ Result := False;
+ ErrorMessage := 'Can''t read BPM from Song';
+ end;
+ end
+
+ Else If (AValues[I].Name = 'RESOLUTION') then
+ begin
+ AValues[I].Value := Uppercase(AValues[I].Value);
+ //Ultrastar Resolution is "how often a Beat is split / 4"
+ If (AValues[I].Value = 'HEMIDEMISEMIQUAVER') then
+ SongInfo.Header.Resolution := 64 div 4
+ Else If (AValues[I].Value = 'DEMISEMIQUAVER') then
+ SongInfo.Header.Resolution := 32 div 4
+ Else If (AValues[I].Value = 'SEMIQUAVER') then
+ SongInfo.Header.Resolution := 16 div 4
+ Else If (AValues[I].Value = 'QUAVER') then
+ SongInfo.Header.Resolution := 8 div 4
+ Else If (AValues[I].Value = 'CROTCHET') then
+ SongInfo.Header.Resolution := 4 div 4
+ Else
+ begin //Can't understand teh Resolution :/
+ Result := False;
+ ErrorMessage := 'Can''t read Resolution from Song';
+ end;
+ end
+
+ Else If (AValues[I].Name = 'GENRE') then
+ begin
+ SongInfo.Header.Genre := AValues[I].Value;
+ end
+
+ Else If (AValues[I].Name = 'YEAR') then
+ begin
+ SongInfo.Header.Year := AValues[I].Value;
+ end
+
+ Else If (AValues[I].Name = 'VERSION') then
+ begin
+ SingstarVersion := AValues[I].Value;
+ end;
+ end;
+
+ ParserState := PS_Melody; //In Melody Tag
+ end;
+ end;
+
+
+ PS_Melody: begin //Search for Sentence, Artist/Title Info or eo Melody
+ If (Tag = 'SENTENCE') then
+ begin
+ ParserState := PS_Sentence; //Parse in a Sentence Tag now
+
+ //Increase SentenceCount
+ Inc(SongInfo.CountSentences);
+
+ BindLyrics := True; //Don't let Txts Begin w/ Space
+
+ //Search for Duett Singer Info
+ MakeValuesArray;
+ For I := 0 to High(AValues) do
+ If (AValues[I].Name = 'SINGER') then
+ begin
+ AValues[I].Value := Uppercase(AValues[I].Value);
+ If (AValues[I].Value = 'SOLO 1') then
+ CurDuettSinger := DS_Player1
+ Else If (AValues[I].Value = 'SOLO 2') then
+ CurDuettSinger := DS_Player2
+ Else
+ CurDuettSinger := DS_Both; //In case of "Group" or anything that is not identified use Both
+ end;
+ end
+
+ Else If (Tag = '!--') then
+ begin //Comment, this may be Artist or Title Info
+ I := Pos(':', Values); //Search for Delimiter
+
+ If (I <> 0) then //If Found check for Title or Artist
+ begin
+ //Copy Title or Artist Tag to Tag String
+ Tag := Uppercase(Trim(Copy(Values, 1, I - 1)));
+
+ If (Tag = 'ARTIST') then
+ begin
+ SongInfo.Header.Artist := Trim(Copy(Values, I + 1, Length(Values) - I - 2));
+ Inc(SongInfo.ID); //Inc SongID when header Information is added
+ end
+ Else If (Tag = 'TITLE') then
+ begin
+ SongInfo.Header.Title := Trim(Copy(Values, I + 1, Length(Values) - I - 2));
+ Inc(SongInfo.ID); //Inc SongID when header Information is added
+ end;
+ end;
+ end
+
+ //Parsing for weird "Die toten Hosen" Tags
+ Else If (Tag = '!--ARTIST:') OR (Tag = '!--ARTIST') then
+ begin //Comment, with Artist Info
+ I := Pos(':', Values); //Search for Delimiter
+
+ Inc(SongInfo.ID); //Inc SongID when header Information is added
+
+ SongInfo.Header.Artist := Trim(Copy(Values, I + 1, Length(Values) - I - 2));
+ end
+
+ Else If (Tag = '!--TITLE:') OR (Tag = '!--TITLE') then
+ begin //Comment, with Artist Info
+ I := Pos(':', Values); //Search for Delimiter
+
+ Inc(SongInfo.ID); //Inc SongID when header Information is added
+
+ SongInfo.Header.Title := Trim(Copy(Values, I + 1, Length(Values) - I - 2));
+ end
+
+ Else If (Tag = '/MELODY') then
+ begin
+ ParserState := PS_None;
+ Exit; //Stop Parsing, Melody iTag ended
+ end
+ end;
+
+
+ PS_Sentence: begin //Search for Notes or eo Sentence
+ If (Tag = 'NOTE') then
+ begin //Found Note
+ //Get Values
+ MakeValuesArray;
+
+ NoteType := NT_Normal;
+ For I := 0 to High(AValues) do
+ begin
+ If (AValues[I].Name = 'DURATION') then
+ begin
+ Duration := StrtoIntDef(AValues[I].Value, -1);
+ If (Duration < 0) then
+ begin
+ Result := False;
+ ErrorMessage := 'Can''t read duration from Note in Line: "' + Line + '"';
+ Exit;
+ end;
+ end
+ Else If (AValues[I].Name = 'MIDINOTE') then
+ begin
+ Tone := StrtoIntDef(AValues[I].Value, 0);
+ end
+ Else If (AValues[I].Name = 'BONUS') AND (Uppercase(AValues[I].Value) = 'YES') then
+ begin
+ NoteType := NT_Golden;
+ end
+ Else If (AValues[I].Name = 'FREESTYLE') AND (Uppercase(AValues[I].Value) = 'YES') then
+ begin
+ NoteType := NT_Freestyle;
+ end
+ Else If (AValues[I].Name = 'LYRIC') then
+ begin
+ Lyric := AValues[I].Value;
+
+ If (Length(Lyric) > 0) then
+ begin
+ If (Lyric = '-') then
+ Lyric[1] := Settings.DashReplacement;
+
+ If (not BindLyrics) then
+ Lyric := ' ' + Lyric;
+
+
+ If (Length(Lyric) > 2) AND (Lyric[Length(Lyric)-1] = ' ') AND (Lyric[Length(Lyric)] = '-') then
+ begin //Between this and the next Lyric should be no space
+ BindLyrics := True;
+ SetLength(Lyric, Length(Lyric) - 2);
+ end
+ else
+ BindLyrics := False; //There should be a Space
+ end;
+ end;
+ end;
+
+ //Add Note
+ I := SongInfo.CountSentences - 1;
+
+ If (Length(Lyric) > 0) then
+ begin //Real note, no rest
+ //First Note of Sentence
+ If (Length(SongInfo.Sentences) < SongInfo.CountSentences) then
+ begin
+ SetLength(SongInfo.Sentences, SongInfo.CountSentences);
+ SetLength(SongInfo.Sentences[I].Notes, 0);
+ end;
+
+ //First Note of Song -> Generate Gap
+ If (FirstNote) then
+ begin
+ //Calculate Gap
+ If (SongInfo.Header.Resolution <> 0) AND (SongInfo.Header.BPM <> 0) then
+ SongInfo.Header.Gap := Round(CurPosinSong / (SongInfo.Header.BPM*SongInfo.Header.Resolution) * 60000)
+ Else
+ begin
+ Result := False;
+ ErrorMessage := 'Can''t calculate Gap, no Resolution or BPM present.';
+ Exit;
+ end;
+
+ CurPosinSong := 0; //Start at 0, because Gap goes until here
+ Inc(SongInfo.ID); //Add Header Value therefore Inc
+ FirstNote := False;
+ end;
+
+ J := Length(SongInfo.Sentences[I].Notes);
+ SetLength(SongInfo.Sentences[I].Notes, J + 1);
+ SongInfo.Sentences[I].Notes[J].Start := CurPosinSong;
+ SongInfo.Sentences[I].Notes[J].Duration := Duration;
+ SongInfo.Sentences[I].Notes[J].Tone := Tone;
+ SongInfo.Sentences[I].Notes[J].NoteTyp := NoteType;
+ SongInfo.Sentences[I].Notes[J].Lyric := Lyric;
+
+ //Inc Pos in Song
+ Inc(CurPosInSong, Duration);
+ end
+ else
+ begin
+ //just change pos in Song
+ Inc(CurPosInSong, Duration);
+ end;
+
+
+ end
+ Else If (Tag = '/SENTENCE') then
+ begin //End of Sentence Tag
+ ParserState := PS_Melody;
+
+ //Delete Sentence if no Note is Added
+ If (Length(SongInfo.Sentences) <> SongInfo.CountSentences) then
+ begin
+ SongInfo.CountSentences := Length(SongInfo.Sentences);
+ end;
+ end;
+ end;
+ end;
+
+ end
+ else //Empty Line -> parsed succesful ;)
+ Result := true;
+end;
+
+Function TParser.ParseConfigforEdition(const Filename: String): String;
+var
+ txt: TStringlist;
+ I: Integer;
+ J, K: Integer;
+ S: String;
+begin
+ Result := '';
+ txt := TStringlist.Create;
+ try
+ txt.LoadFromFile(Filename);
+
+ For I := 0 to txt.Count-1 do
+ begin
+ S := Trim(txt.Strings[I]);
+ J := Pos('<PRODUCT_NAME>', S);
+
+ If (J <> 0) then
+ begin
+ Inc(J, 14);
+ K := Pos('</PRODUCT_NAME>', S);
+ If (K<J) then K := Length(S) + 1;
+
+ Result := Copy(S, J, K - J);
+ Break;
+ end;
+ end;
+
+ Edition := Result;
+ finally
+ txt.Free;
+ end;
+end;
+
+end.