aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorf1fth_freed0m <f1fth_freed0m@b956fd51-792f-4845-bead-9b4dfca2ff2c>2008-03-27 22:30:01 +0000
committerf1fth_freed0m <f1fth_freed0m@b956fd51-792f-4845-bead-9b4dfca2ff2c>2008-03-27 22:30:01 +0000
commitf03ed07bc00ddc28a77f4e81d25e7d3ee596e06d (patch)
treee635c761efeee38928cb2d6089c4d4ba3054dc68
parentbbf6784e26b5f94c87c9deedb75711948d937f46 (diff)
downloadusdx-f03ed07bc00ddc28a77f4e81d25e7d3ee596e06d.tar.gz
usdx-f03ed07bc00ddc28a77f4e81d25e7d3ee596e06d.tar.xz
usdx-f03ed07bc00ddc28a77f4e81d25e7d3ee596e06d.zip
USDX is now able to load SingStar XML Songfiles using Whitesharks XML2US Parser
git-svn-id: svn://svn.code.sf.net/p/ultrastardx/svn/trunk@974 b956fd51-792f-4845-bead-9b4dfca2ff2c
-rw-r--r--Game/Code/Classes/USong.pas307
-rw-r--r--Game/Code/Classes/USongs.pas72
-rw-r--r--Game/Code/Screens/UScreenEditSub.pas5
-rw-r--r--Game/Code/Screens/UScreenSing.pas5
-rw-r--r--Game/Code/UltraStar.dpr1
5 files changed, 360 insertions, 30 deletions
diff --git a/Game/Code/Classes/USong.pas b/Game/Code/Classes/USong.pas
index 5b10f8a1..ade511a8 100644
--- a/Game/Code/Classes/USong.pas
+++ b/Game/Code/Classes/USong.pas
@@ -30,7 +30,8 @@ uses
{$IFDEF USE_PSEUDO_THREAD}
PseudoThread,
{$ENDIF}
- UCatCovers;
+ UCatCovers,
+ UXMLSong;
type
@@ -54,6 +55,7 @@ type
procedure NewSentence(LineNumberP: integer; Param1, Param2: integer);
function ReadTXTHeader( const aFileName : WideString ): boolean;
+ function ReadXMLHeader( const aFileName : WideString ): boolean;
public
Path: widestring;
Folder: widestring; // for sorting by folder
@@ -105,7 +107,9 @@ type
constructor create ( const aFileName : WideString );
function LoadSong: boolean;
+ function LoadXMLSong: boolean;
function Analyse(): boolean;
+ function AnalyseXML(): boolean;
procedure clear();
end;
@@ -155,7 +159,7 @@ begin
end;
-
+//Load TXT Song
function TSong.LoadSong(): boolean;
var
@@ -202,7 +206,7 @@ begin
if (self.FileName = '') then
self.Filename := ExtractFileName(FileName);
-
+
Result := False;
Reset(SongFile);
@@ -211,7 +215,7 @@ begin
repeat
ReadLn(SongFile, Text);
Inc(FileLineNo);
-
+
if (EoF(SongFile)) then
begin //Song File Corrupted - No Notes
CloseFile(SongFile);
@@ -248,6 +252,7 @@ begin
Read(SongFile, Param3);
Read(SongFile, ParamS);
+
//Check for ZeroNote
if Param2 = 0 then Log.LogError('Found ZeroNote at "'+TempC+' '+IntToStr(Param1)+' '+IntToStr(Param2)+' '+IntToStr(Param3)+ParamS+'" -> Note ignored!') else
begin
@@ -260,7 +265,7 @@ begin
ParseNote(0, TempC, (Param1+Rel[0]) * Mult, Param2 * Mult, Param3, ParamS);
ParseNote(1, TempC, (Param1+Rel[1]) * Mult, Param2 * Mult, Param3, ParamS);
end;
- end; //Zeronote check
+ end; //Zeronote check
end; // if
if TempC = '-' then
@@ -319,11 +324,11 @@ begin
Read(SongFile, TempC);
Inc(FileLineNo);
end; // while}
-
+
for Count := 0 to High(Lines) do begin
Lines[Count].Line[High(Lines[Count].Line)].LastLine := True;
end;
-
+
CloseFile(SongFile);
except
try
@@ -339,6 +344,272 @@ begin
Result := true;
end;
+//Load XML Song
+function TSong.LoadXMLSong(): boolean;
+var
+ //TempC: char;
+ Text: string;
+ CP: integer; // Current Player (0 or 1)
+ Count: integer;
+ Both: boolean;
+ Param1: integer;
+ Param2: integer;
+ Param3: integer;
+ ParamS: string;
+ I,J,X: Integer;
+
+ NoteType: Char;
+ SentenceEnd, Rest, Time: Integer;
+ Parser: TParser;
+begin
+ Result := false;
+
+ if not FileExists(Path + PathDelim + FileName) then
+ begin
+ Log.LogError('File not found: "' + Path + PathDelim + FileName + '"', 'TSong.LoadSong()');
+ exit;
+ end;
+
+ MultBPM := 4; // multiply beat-count of note by 4
+ Mult := 1; // accuracy of measurement of note
+ Base[0] := 100; // high number
+ Lines[0].NoteType := 0;
+ self.Relative := false;
+ Rel[0] := 0;
+ CP := 0;
+ Both := false;
+
+ if Length(Player) = 2 then
+ Both := true;
+
+
+ Parser := TParser.Create;
+ Parser.Settings.DashReplacement := '~';
+
+
+ for Count := 0 to High(Lines) do begin
+ SetLength(Lines[Count].Line, 1);
+ Lines[Count].High := 0;
+ Lines[Count].Number := 1;
+ Lines[Count].Current := 0;
+ Lines[Count].Resolution := self.Resolution;
+ Lines[Count].NotesGAP := self.NotesGAP;
+ Lines[Count].Line[0].IlNut := 0;
+ Lines[Count].Line[0].HighNote := -1;
+ Lines[Count].Line[0].LastLine := False;
+ end;
+
+
+//Try to Parse the Song
+If Parser.ParseSong(Path + PathDelim + FileName) then
+ begin
+ Writeln('XML Inputfile Parsed succesful');
+ //Start write parsed information to Song
+ //Notes Part
+ For I := 0 to High(Parser.SongInfo.Sentences) do
+ begin
+ //Add Notes
+ For J := 0 to High(Parser.SongInfo.Sentences[I].Notes) do
+ begin
+ Case Parser.SongInfo.Sentences[I].Notes[J].NoteTyp of
+ NT_Normal: NoteType := ':';
+ NT_Golden: NoteType := '*';
+ NT_Freestyle: NoteType := 'F';
+ end;
+
+ Param1:=Parser.SongInfo.Sentences[I].Notes[J].Start; //Note Start
+ Param2:=Parser.SongInfo.Sentences[I].Notes[J].Duration; //Note Duration
+ Param3:=Parser.SongInfo.Sentences[I].Notes[J].Tone; //Note Tone
+ ParamS:=' ' + Parser.SongInfo.Sentences[I].Notes[J].Lyric; //Note Lyric
+
+
+ if not Both then
+ // P1
+ ParseNote(0, NoteType, (Param1+Rel[0]) * Mult, Param2 * Mult, Param3, ParamS)
+ else begin
+ // P1 + P2
+ ParseNote(0, NoteType, (Param1+Rel[0]) * Mult, Param2 * Mult, Param3, ParamS);
+ ParseNote(1, NoteType, (Param1+Rel[1]) * Mult, Param2 * Mult, Param3, ParamS);
+ end;
+
+
+ if not Both then
+ begin
+ Lines[CP].Line[Lines[CP].High].BaseNote := Base[CP];
+ Lines[CP].Line[Lines[CP].High].LyricWidth := glTextWidth(PChar(Lines[CP].Line[Lines[CP].High].Lyric));
+ //Total Notes Patch
+ Lines[CP].Line[Lines[CP].High].TotalNotes := 0;
+ for X := low(Lines[CP].Line[Lines[CP].High].Note) to high(Lines[CP].Line[Lines[CP].High].Note) do
+ begin
+ Lines[CP].Line[Lines[CP].High].TotalNotes := Lines[CP].Line[Lines[CP].High].TotalNotes + Lines[CP].Line[Lines[CP].High].Note[X].Length * Lines[CP].Line[Lines[CP].High].Note[X].NoteType;
+ end;
+ //Total Notes Patch End
+ end else begin
+ for Count := 0 to High(Lines) do begin
+ Lines[Count].Line[Lines[Count].High].BaseNote := Base[Count];
+ Lines[Count].Line[Lines[Count].High].LyricWidth := glTextWidth(PChar(Lines[Count].Line[Lines[Count].High].Lyric));
+ //Total Notes Patch
+ Lines[Count].Line[Lines[Count].High].TotalNotes := 0;
+ for X := low(Lines[Count].Line[Lines[Count].High].Note) to high(Lines[Count].Line[Lines[Count].High].Note) do
+ begin
+ Lines[Count].Line[Lines[Count].High].TotalNotes := Lines[Count].Line[Lines[Count].High].TotalNotes + Lines[Count].Line[Lines[Count].High].Note[X].Length * Lines[Count].Line[Lines[Count].High].Note[X].NoteType;
+ end;
+ //Total Notes Patch End
+ end;
+ end;
+
+
+
+ end; //J Forloop
+
+ //Add Sentence break
+ If (I < High(Parser.SongInfo.Sentences)) then
+ begin
+
+ SentenceEnd := Parser.SongInfo.Sentences[I].Notes[High(Parser.SongInfo.Sentences[I].Notes)].Start + Parser.SongInfo.Sentences[I].Notes[High(Parser.SongInfo.Sentences[I].Notes)].Duration;
+ Rest := Parser.SongInfo.Sentences[I+1].Notes[0].Start - SentenceEnd;
+
+ //Calculate Time
+ Case Rest of
+ 0, 1: Time := Parser.SongInfo.Sentences[I+1].Notes[0].Start;
+ 2: Time := Parser.SongInfo.Sentences[I+1].Notes[0].Start - 1;
+ 3: Time := Parser.SongInfo.Sentences[I+1].Notes[0].Start - 2;
+ else
+ If (Rest >= 4) then
+ Time := SentenceEnd + 2
+ Else //Sentence overlapping :/
+ Time := Parser.SongInfo.Sentences[I+1].Notes[0].Start;
+ end;
+ // new sentence
+ if not Both then
+ // P1
+ NewSentence(0, (Time + Rel[0]) * Mult, Param2)
+ else begin
+ // P1 + P2
+ NewSentence(0, (Time + Rel[0]) * Mult, Param2);
+ NewSentence(1, (Time + Rel[1]) * Mult, Param2);
+ end;
+
+ end;
+ end;
+ //End write parsed information to Song
+ Parser.Free;
+ end
+ else
+ begin
+ Log.LogError('Could not parse Inputfile: ' + Path + PathDelim + FileName);
+ exit;
+ end;
+
+ for Count := 0 to High(Lines) do begin
+ Lines[Count].Line[High(Lines[Count].Line)].LastLine := True;
+ end;
+
+ Result := true;
+end;
+
+function TSong.ReadXMLHeader(const aFileName : WideString): boolean;
+var
+ Line, Identifier, Value: String;
+ Temp : word;
+ Done : byte;
+ Parser : TParser;
+begin
+ Result := true;
+ Done := 0;
+
+//Parse XML
+ Parser := TParser.Create;
+ Parser.Settings.DashReplacement := '~';
+
+
+ If Parser.ParseSong(self.Path + self.FileName) then
+ begin
+ //-----------
+ //Required Attributes
+ //-----------
+
+ //Title
+ self.Title := Parser.SongInfo.Header.Title;
+
+ //Add Title Flag to Done
+ Done := Done or 1;
+
+ //Artist
+ self.Artist := Parser.SongInfo.Header.Artist;
+
+ //Add Artist Flag to Done
+ Done := Done or 2;
+
+ //MP3 File //Test if Exists
+ self.Mp3 := platform.FindSongFile(Path, '*.mp3');
+ if (FileExists(self.Path + self.Mp3)) then
+ //Add Mp3 Flag to Done
+ Done := Done or 4;
+
+ //Beats per Minute
+ SetLength(self.BPM, 1);
+ self.BPM[0].StartBeat := 0;
+
+ self.BPM[0].BPM := (Parser.SongInfo.Header.BPM * Parser.SongInfo.Header.Resolution/4 ) * Mult * MultBPM;
+
+ if self.BPM[0].BPM <> 0 then
+ //Add BPM Flag to Done
+ Done := Done or 8;
+
+ //---------
+ //Additional Header Information
+ //---------
+
+ // Gap
+ self.GAP := Parser.SongInfo.Header.Gap;
+
+ //Cover Picture
+ self.Cover := platform.FindSongFile(Path, '*[CO].jpg');
+
+ //Background Picture
+ self.Background := platform.FindSongFile(Path, '*[BG].jpg');
+
+ // Video File
+ // self.Video := Value
+
+ // Video Gap
+ // self.VideoGAP := song_StrtoFloat( Value )
+
+ //Genre Sorting
+ self.Genre := Parser.SongInfo.Header.Genre;
+
+ //Edition Sorting
+ self.Edition := Parser.SongInfo.Header.Edition;
+
+ //Year Sorting
+ //Parser.SongInfo.Header.Year
+
+ //Language Sorting
+ self.Language := Parser.SongInfo.Header.Language;
+ end else
+ Log.LogError('File Incomplete or not SingStar XML (A): ' + aFileName);
+
+ Parser.Free;
+
+ //Check if all Required Values are given
+ if (Done <> 15) then
+ begin
+ Result := False;
+ if (Done and 8) = 0 then //No BPM Flag
+ Log.LogError('BPM Tag Missing: ' + self.FileName)
+ else if (Done and 4) = 0 then //No MP3 Flag
+ Log.LogError('MP3 Tag/File Missing: ' + self.FileName)
+ else if (Done and 2) = 0 then //No Artist Flag
+ Log.LogError('Artist Tag Missing: ' + self.FileName)
+ else if (Done and 1) = 0 then //No Title Flag
+ Log.LogError('Title Tag Missing: ' + self.FileName)
+ else //unknown Error
+ Log.LogError('File Incomplete or not SingStar XML (B - '+ inttostr(Done) +'): ' + aFileName);
+ end;
+
+end;
+
function TSong.ReadTXTHeader(const aFileName : WideString): boolean;
@@ -348,7 +619,7 @@ function TSong.ReadTXTHeader(const aFileName : WideString): boolean;
// lOldDecimalSeparator : Char; // Auto Removed, Unused Variable
begin
lValue := aValue;
-
+
if (Pos(',', lValue) <> 0) then
lValue[Pos(',', lValue)] := '.';
@@ -398,7 +669,7 @@ begin
{$IFDEF UTF8_FILENAMES}
if ((Identifier = 'MP3') or (Identifier = 'BACKGROUND') or (Identifier = 'COVER') or (Identifier = 'VIDEO')) then
- Value := Utf8Encode(Value);
+ Value := Utf8Encode(Value);
{$ENDIF}
//Title
@@ -448,7 +719,7 @@ begin
//Additional Header Information
//---------
- // Video Gap
+ // Gap
else if (Identifier = 'GAP') then
self.GAP := song_StrtoFloat( Value )
@@ -657,7 +928,7 @@ begin
end
else
Lines[LineNumberP].Line[Lines[LineNumberP].High].Start := Param1;
-
+
Lines[LineNumberP].Line[Lines[LineNumberP].High].LastLine := False;
Base[LineNumberP] := 100; // high number
@@ -723,5 +994,19 @@ begin
end;
+function TSong.AnalyseXML(): boolean;
+begin
+ Result := False;
+
+ //Reset LineNo
+ FileLineNo := 0;
+
+ //Clear old Song Header
+ self.clear;
+
+ //Read Header
+ Result := self.ReadXMLHeader( FileName );
+
+end;
end.
diff --git a/Game/Code/Classes/USongs.pas b/Game/Code/Classes/USongs.pas
index 10af65e3..df748e74 100644
--- a/Game/Code/Classes/USongs.pas
+++ b/Game/Code/Classes/USongs.pas
@@ -81,6 +81,8 @@ type
procedure LoadSongList; // load all songs
procedure BrowseDir(Dir: widestring); // should return number of songs in the future
+ procedure BrowseTXTFiles(Dir: widestring);
+ procedure BrowseXMLFiles(Dir: widestring);
procedure Sort(Order: integer);
function FindSongFile(Dir, Mask: widestring): widestring;
property Processing : boolean read fProcessing;
@@ -123,7 +125,7 @@ const
IN_CREATE = $00000100; //* Subfile was created */
IN_DELETE = $00000200; //* Subfile was deleted */
IN_DELETE_SELF = $00000400; //* Self was deleted */
-
+
implementation
@@ -191,7 +193,7 @@ begin
self.suspend;
end;
-{$ENDIF}
+{$ENDIF}
end;
procedure TSongs.int_LoadSongList;
@@ -232,7 +234,7 @@ begin
finally
Log.LogError('SongList', 'Search Complete');
-
+
fParseSongDirectory := false;
fProcessing := false;
end;
@@ -246,37 +248,73 @@ begin
end;
procedure TSongs.BrowseDir(Dir: widestring);
+begin
+ BrowseTXTFiles(Dir);
+ BrowseXMLFiles(Dir);
+end;
+
+procedure TSongs.BrowseTXTFiles(Dir: widestring);
var
- i : Integer;
- Files : TDirectoryEntryArray;
- lSong : TSong;
+ i : Integer;
+ Files : TDirectoryEntryArray;
+ lSong : TSong;
begin
- Files := Platform.DirectoryFindFiles( Dir, '.txt', true);
-
+ Files := Platform.DirectoryFindFiles( Dir, '.txt', true);
+
for i := 0 to Length(Files)-1 do
begin
if Files[i].IsDirectory then
begin
- BrowseDir( Dir + Files[i].Name + PathDelim );
+ BrowseTXTFiles( Dir + Files[i].Name + PathDelim ); //Recursive Call
end
else
begin
lSong := TSong.create( Dir + Files[i].Name );
if NOT lSong.Analyse then
- begin
- Log.LogError('AnalyseFile failed for "' + Files[i].Name + '".');
- freeandnil( lSong );
- end
- else
- begin
- SongList.add( lSong );
- end;
+ begin
+ Log.LogError('AnalyseFile failed for "' + Files[i].Name + '".');
+ freeandnil( lSong );
+ end
+ else SongList.add( lSong );
+
+ end;
+ end;
+ SetLength( Files, 0);
+
+end;
+
+procedure TSongs.BrowseXMLFiles(Dir: widestring);
+var
+ i : Integer;
+ Files : TDirectoryEntryArray;
+ lSong : TSong;
+begin
+
+ Files := Platform.DirectoryFindFiles( Dir, '.xml', true);
+
+ for i := 0 to Length(Files)-1 do
+ begin
+ if Files[i].IsDirectory then
+ begin
+ BrowseXMLFiles( Dir + Files[i].Name + PathDelim ); //Recursive Call
+ end
+ else
+ begin
+ lSong := TSong.create( Dir + Files[i].Name );
+
+ if NOT lSong.AnalyseXML then
+ begin
+ Log.LogError('AnalyseFile failed for "' + Files[i].Name + '".');
+ freeandnil( lSong );
+ end
+ else SongList.add( lSong );
end;
end;
SetLength( Files, 0);
+
end;
procedure TSongs.Sort(Order: integer);
diff --git a/Game/Code/Screens/UScreenEditSub.pas b/Game/Code/Screens/UScreenEditSub.pas
index 1fb7bff8..bf479c41 100644
--- a/Game/Code/Screens/UScreenEditSub.pas
+++ b/Game/Code/Screens/UScreenEditSub.pas
@@ -1170,7 +1170,10 @@ begin
ResetSingTemp;
try
- Error := not CurrentSong.LoadSong();
+ //Check if File is XML
+ if copy(CurrentSong.FileName,length(CurrentSong.FileName)-3,4) = '.xml'
+ then Error := not CurrentSong.LoadXMLSong()
+ else Error := not CurrentSong.LoadSong();
except
Error := True;
end;
diff --git a/Game/Code/Screens/UScreenSing.pas b/Game/Code/Screens/UScreenSing.pas
index d75a3171..859aeb52 100644
--- a/Game/Code/Screens/UScreenSing.pas
+++ b/Game/Code/Screens/UScreenSing.pas
@@ -462,7 +462,10 @@ begin
// FIXME: bad style, put the try-except into LoadSong() and not here
try
- success := CurrentSong.LoadSong();
+ //Check if File is XML
+ if copy(CurrentSong.FileName,length(CurrentSong.FileName)-3,4) = '.xml'
+ then success := CurrentSong.LoadXMLSong()
+ else success := CurrentSong.LoadSong();
except
success := false;
end;
diff --git a/Game/Code/UltraStar.dpr b/Game/Code/UltraStar.dpr
index 79f9675e..aa92d8fa 100644
--- a/Game/Code/UltraStar.dpr
+++ b/Game/Code/UltraStar.dpr
@@ -98,6 +98,7 @@ uses
UTime in 'Classes\UTime.pas',
TextGL in 'Classes\TextGL.pas',
USong in 'Classes\USong.pas',
+ UXMLSong in 'Classes\UXMLSong.pas',
USongs in 'Classes\USongs.pas',
UIni in 'Classes\UIni.pas',
ULyrics in 'Classes\ULyrics.pas',