diff options
author | whiteshark0 <whiteshark0@b956fd51-792f-4845-bead-9b4dfca2ff2c> | 2009-12-11 17:34:54 +0000 |
---|---|---|
committer | whiteshark0 <whiteshark0@b956fd51-792f-4845-bead-9b4dfca2ff2c> | 2009-12-11 17:34:54 +0000 |
commit | 1ab628e8ad6c85c8f1b562f10480253ee3e622b7 (patch) | |
tree | d21621f68850ecd7762137e1c4387fa15731a811 /Lua/src | |
parent | 6ec275387c320d3d9a8f5b6fe185687643565b8c (diff) | |
download | usdx-1ab628e8ad6c85c8f1b562f10480253ee3e622b7.tar.gz usdx-1ab628e8ad6c85c8f1b562f10480253ee3e622b7.tar.xz usdx-1ab628e8ad6c85c8f1b562f10480253ee3e622b7.zip |
merged trunk into lua branch
plugin loading is disabled atm because of a bug reading the files (lua may be the reason).
Reading the files in usdx and passing the contents to lua may solve this
git-svn-id: svn://svn.code.sf.net/p/ultrastardx/svn/branches/experimental@2019 b956fd51-792f-4845-bead-9b4dfca2ff2c
Diffstat (limited to '')
184 files changed, 39653 insertions, 14486 deletions
diff --git a/Lua/src/.gitignore b/Lua/src/.gitignore new file mode 100644 index 00000000..1c20f512 --- /dev/null +++ b/Lua/src/.gitignore @@ -0,0 +1,8 @@ +*.cfg
+*.local
+*.bdsproj
+*.identcache
+clean.bat
+config-linux.inc
+paths.inc
+ultrastardx-*.lp[i|s]
diff --git a/Lua/src/Makefile.in b/Lua/src/Makefile.in index 3d4b6def..06e62c43 100644 --- a/Lua/src/Makefile.in +++ b/Lua/src/Makefile.in @@ -122,7 +122,7 @@ endif LIBS ?= @LIBS@ LDFLAGS ?= @LDFLAGS@ -linkflags := $(sort $(LDFLAGS) $(LIBS)) +linkflags := -L/usr/lib $(sort $(LDFLAGS) $(LIBS)) ifneq ($(linkflags),) PLINKFLAGS := -k"$(linkflags)" endif diff --git a/Lua/src/base/TextGL.pas b/Lua/src/base/TextGL.pas index bd505f51..7fe98d29 100644 --- a/Lua/src/base/TextGL.pas +++ b/Lua/src/base/TextGL.pas @@ -33,169 +33,101 @@ interface {$I switches.inc} -// as long as the transition to freetype is not finished -// use the old implementation -{$IFDEF UseFreetype} - {$INCLUDE TextGLFreetype.pas} -{$ELSE} uses gl, + glext, SDL, + Classes, UTexture, + UFont, + UPath, ULog; +type + PGLFont = ^TGLFont; + TGLFont = record + Font: TScalableFont; + X, Y, Z: real; + end; + +var + Fonts: array of TGLFont; + ActFont: integer; + procedure BuildFont; // build our bitmap font procedure KillFont; // delete the font -function glTextWidth(const text: string): real; // returns text width -procedure glPrint(const text: string); // custom GL "Print" routine +function glTextWidth(const text: UTF8String): real; // returns text width +procedure glPrint(const text: UTF8String); // custom GL "Print" routine procedure ResetFont(); // reset font settings of active font procedure SetFontPos(X, Y: real); // sets X and Y procedure SetFontZ(Z: real); // sets Z procedure SetFontSize(Size: real); procedure SetFontStyle(Style: integer); // sets active font style (normal, bold, etc) procedure SetFontItalic(Enable: boolean); // sets italic type letter (works for all fonts) -procedure SetFontAspectW(Aspect: real); procedure SetFontReflection(Enable:boolean;Spacing: real); // enables/disables text reflection -//function NextPowerOfTwo(Value: integer): integer; -// Checks if the ttf exists, if yes then a SDL_ttf is returned -//function LoadFont(FileName: PAnsiChar; PointSize: integer):PTTF_Font; -// Does the renderstuff, color is in $ffeecc style -//function RenderText(font: PTTF_Font; Text:PAnsiChar; Color: Cardinal):PSDL_Surface; - -type - TTextGL = record - X: real; - Y: real; - Z: real; - Text: string; - Size: real; - ColR: real; - ColG: real; - ColB: real; - end; - - PFont = ^TFont; - TFont = record - Tex: TTexture; - Width: array[0..255] of byte; - AspectW: real; - Centered: boolean; - Outline: real; - Italic: boolean; - Reflection: boolean; - ReflectionSpacing: real; - end; - - -var - Fonts: array of TFont; - ActFont: integer; - - implementation uses - UMain, - UCommon, + UTextEncoding, SysUtils, IniFiles, - Classes, - UGraphic; - -var - // Colours for the reflection - TempColor: array[0..3] of GLfloat; + UCommon, + UMain, + UPathUtils; -{** - * Load font info. - * FontFile is the name of the image (.png) not the data (.dat) file - *} -procedure LoadFontInfo(FontID: integer; const FontFile: string); +function FindFontFile(FontIni: TCustomIniFile; Font: string): IPath; var - Stream: TFileStream; - DatFile: string; + Filename: IPath; begin - DatFile := ChangeFileExt(FontFile, '.dat'); - FillChar(Fonts[FontID].Width[0], Length(Fonts[FontID].Width), 0); - - Stream := nil; - try - Stream := TFileStream.Create(DatFile, fmOpenRead); - Stream.Read(Fonts[FontID].Width, 256); - except - Log.LogError('Error while reading font['+ inttostr(FontID) +']', 'LoadFontInfo'); - end; - Stream.Free; + Filename := Path(FontIni.ReadString(Font, 'File', '')); + Result := FontPath.Append(Filename); + // if path does not exist, try as an absolute path + if (not Result.IsFile) then + Result := Filename; end; -// Builds bitmap fonts procedure BuildFont; var - Count: integer; FontIni: TMemIniFile; - FontFile: string; // filename of the image (with .png/... ending) + FontFile: IPath; begin ActFont := 0; SetLength(Fonts, 4); - FontIni := TMemIniFile.Create(FontPath + 'fonts.ini'); - - // Normal - - FontFile := FontPath + FontIni.ReadString('Normal', 'File', ''); - - Fonts[0].Tex := Texture.LoadTexture(true, FontFile, TEXTURE_TYPE_TRANSPARENT, 0); - Fonts[0].Tex.H := 30; - Fonts[0].AspectW := 0.9; - Fonts[0].Outline := 0; - - LoadFontInfo(0, FontFile); - - // Bold + FontIni := TMemIniFile.Create(FontPath.Append('fonts.ini').ToNative); - FontFile := FontPath + FontIni.ReadString('Bold', 'File', ''); - - Fonts[1].Tex := Texture.LoadTexture(true, FontFile, TEXTURE_TYPE_TRANSPARENT, 0); - Fonts[1].Tex.H := 30; - Fonts[1].AspectW := 1; - Fonts[1].Outline := 0; - - LoadFontInfo(1, FontFile); - for Count := 0 to 255 do - Fonts[1].Width[Count] := Fonts[1].Width[Count] div 2; - - // Outline1 - - FontFile := FontPath + FontIni.ReadString('Outline1', 'File', ''); - - Fonts[2].Tex := Texture.LoadTexture(true, FontFile, TEXTURE_TYPE_TRANSPARENT, 0); - Fonts[2].Tex.H := 30; - Fonts[2].AspectW := 0.95; - Fonts[2].Outline := 5; - - LoadFontInfo(2, FontFile); - for Count := 0 to 255 do - Fonts[2].Width[Count] := Fonts[2].Width[Count] div 2 + 2; + try - // Outline2 + // Normal + FontFile := FindFontFile(FontIni, 'Normal'); + Fonts[0].Font := TFTScalableFont.Create(FontFile, 64); + //Fonts[0].Font.GlyphSpacing := 1.4; + //Fonts[0].Font.Aspect := 1.2; - FontFile := FontPath + FontIni.ReadString('Outline2', 'File', ''); + // Bold + FontFile := FindFontFile(FontIni, 'Bold'); + Fonts[1].Font := TFTScalableFont.Create(FontFile, 64); - Fonts[3].Tex := Texture.LoadTexture(true, FontFile, TEXTURE_TYPE_TRANSPARENT, 0); - Fonts[3].Tex.H := 30; - Fonts[3].AspectW := 0.95; - Fonts[3].Outline := 4; + // Outline1 + FontFile := FindFontFile(FontIni, 'Outline1'); + Fonts[2].Font := TFTScalableOutlineFont.Create(FontFile, 64, 0.06); + //TFTScalableOutlineFont(Fonts[2].Font).SetOutlineColor(0.3, 0.3, 0.3); - LoadFontInfo(3, FontFile); - for Count := 0 to 255 do - Fonts[3].Width[Count] := Fonts[3].Width[Count] + 1; + // Outline2 + FontFile := FindFontFile(FontIni, 'Outline2'); + Fonts[3].Font := TFTScalableOutlineFont.Create(FontFile, 64, 0.08); + except + on E: Exception do + Log.LogCritical(E.Message, 'BuildFont'); + end; // close ini-file FontIni.Free; end; + // Deletes the font procedure KillFont; begin @@ -203,133 +135,31 @@ begin //glDeleteLists(..., 256); end; -function glTextWidth(const text: string): real; +function glTextWidth(const text: UTF8String): real; var - Letter: char; - i: integer; - Font: PFont; + Bounds: TBoundsDbl; begin - Result := 0; - Font := @Fonts[ActFont]; - - for i := 1 to Length(text) do - begin - Letter := Text[i]; - Result := Result + Font.Width[Ord(Letter)] * Font.Tex.H / 30 * Font.AspectW; - end; - - if ((Result > 0) and Font.Italic) then - Result := Result + 12 * Font.Tex.H / 60 * Font.AspectW; -end; - -procedure glPrintLetter(Letter: char); -var - TexX, TexY: real; - TexR, TexB: real; - TexHeight: real; - FWidth: real; - PL, PT: real; - PR, PB: real; - XItal: real; // X shift for italic type letter - ReflectionSpacing: real; // Distance of the reflection - Font: PFont; - Tex: PTexture; -begin - Font := @Fonts[ActFont]; - Tex := @Font.Tex; - - FWidth := Font.Width[Ord(Letter)]; - - Tex.W := FWidth * (Tex.H/30) * Font.AspectW; - - // set texture positions - TexX := (ord(Letter) mod 16) * 1/16 + 1/32 - FWidth/1024 - Font.Outline/1024; - TexY := (ord(Letter) div 16) * 1/16 + 2/1024; - TexR := (ord(Letter) mod 16) * 1/16 + 1/32 + FWidth/1024 + Font.Outline/1024; - TexB := (1 + ord(Letter) div 16) * 1/16 - 2/1024; - - TexHeight := TexB - TexY; - - // set vector positions - PL := Tex.X - Font.Outline * (Tex.H/30) * Font.AspectW /2; - PT := Tex.Y; - PR := PL + Tex.W + Font.Outline * (Tex.H/30) * Font.AspectW; - PB := PT + Tex.H; - - if (not Font.Italic) then - XItal := 0 - else - XItal := 12; - - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - - glEnable(GL_TEXTURE_2D); - glBindTexture(GL_TEXTURE_2D, Tex.TexNum); - - glBegin(GL_QUADS); - glTexCoord2f(TexX, TexY); glVertex2f(PL+XItal, PT); - glTexCoord2f(TexX, TexB); glVertex2f(PL, PB); - glTexCoord2f(TexR, TexB); glVertex2f(PR, PB); - glTexCoord2f(TexR, TexY); glVertex2f(PR+XItal, PT); - glEnd; - - // <mog> Reflection - // Yes it would make sense to put this in an extra procedure, - // but this works, doesn't take much lines, and is almost lightweight - if Font.Reflection then - begin - ReflectionSpacing := Font.ReflectionSpacing + Tex.H/2; - - glDepthRange(0, 10); - glDepthFunc(GL_LEQUAL); - glEnable(GL_DEPTH_TEST); - - glBegin(GL_QUADS); - glColor4f(TempColor[0], TempColor[1], TempColor[2], 0); - glTexCoord2f(TexX, TexY + TexHeight/2); - glVertex3f(PL, PB + ReflectionSpacing - Tex.H/2, Tex.z); - - glColor4f(TempColor[0], TempColor[1], TempColor[2], Tex.Alpha-0.3); - glTexCoord2f(TexX, TexB ); - glVertex3f(PL + XItal, PT + ReflectionSpacing, Tex.z); - - glTexCoord2f(TexR, TexB ); - glVertex3f(PR + XItal, PT + ReflectionSpacing, Tex.z); - - glColor4f(TempColor[0], TempColor[1], TempColor[2], 0); - glTexCoord2f(TexR, TexY + TexHeight/2); - glVertex3f(PR, PB + ReflectionSpacing - Tex.H/2, Tex.z); - glEnd; - - glDisable(GL_DEPTH_TEST); - end; // reflection - - glDisable(GL_TEXTURE_2D); - glDisable(GL_BLEND); - - Tex.X := Tex.X + Tex.W; - - //write the colour back - glColor4fv(@TempColor); + Bounds := Fonts[ActFont].Font.BBox(Text, true); + Result := Bounds.Right - Bounds.Left; end; // Custom GL "Print" Routine -procedure glPrint(const Text: string); +procedure glPrint(const Text: UTF8String); var - Pos: integer; + GLFont: PGLFont; begin // if there is no text do nothing if (Text = '') then Exit; - //Save the actual color and alpha (for reflection) - glGetFloatv(GL_CURRENT_COLOR, @TempColor); + GLFont := @Fonts[ActFont]; - for Pos := 1 to Length(Text) do - begin - glPrintLetter(Text[Pos]); - end; + glPushMatrix(); + // set font position + glTranslatef(GLFont.X, GLFont.Y + GLFont.Font.Ascender, GLFont.Z); + // draw string + GLFont.Font.Print(Text); + glPopMatrix(); end; procedure ResetFont(); @@ -342,18 +172,18 @@ end; procedure SetFontPos(X, Y: real); begin - Fonts[ActFont].Tex.X := X; - Fonts[ActFont].Tex.Y := Y; + Fonts[ActFont].X := X; + Fonts[ActFont].Y := Y; end; procedure SetFontZ(Z: real); begin - Fonts[ActFont].Tex.Z := Z; + Fonts[ActFont].Z := Z; end; procedure SetFontSize(Size: real); begin - Fonts[ActFont].Tex.H := Size; + Fonts[ActFont].Font.Height := Size; end; procedure SetFontStyle(Style: integer); @@ -363,21 +193,19 @@ end; procedure SetFontItalic(Enable: boolean); begin - Fonts[ActFont].Italic := Enable; -end; - -procedure SetFontAspectW(Aspect: real); -begin - Fonts[ActFont].AspectW := Aspect; + if (Enable) then + Fonts[ActFont].Font.Style := Fonts[ActFont].Font.Style + [Italic] + else + Fonts[ActFont].Font.Style := Fonts[ActFont].Font.Style - [Italic] end; procedure SetFontReflection(Enable: boolean; Spacing: real); begin - Fonts[ActFont].Reflection := Enable; - Fonts[ActFont].ReflectionSpacing := Spacing; + if (Enable) then + Fonts[ActFont].Font.Style := Fonts[ActFont].Font.Style + [Reflect] + else + Fonts[ActFont].Font.Style := Fonts[ActFont].Font.Style - [Reflect]; + Fonts[ActFont].Font.ReflectionSpacing := Spacing - Fonts[ActFont].Font.Descender; end; end. - -{$ENDIF} - diff --git a/Lua/src/base/TextGLFreetype.pas b/Lua/src/base/TextGLFreetype.pas deleted file mode 100644 index 61b26693..00000000 --- a/Lua/src/base/TextGLFreetype.pas +++ /dev/null @@ -1,222 +0,0 @@ -{* 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: https://ultrastardx.svn.sourceforge.net/svnroot/ultrastardx/trunk/src/base/TextGL.pas $ - * $Id: TextGL.pas 1483 2008-10-28 19:01:20Z tobigun $ - *} - -(* -unit TextGL; - -interface - -{$IFDEF FPC} - {$MODE Delphi} -{$ENDIF} - -{$I switches.inc} -*) - -uses - gl, - glext, - SDL, - UTexture, - UFont, - Classes, - ULog; - -type - PGLFont = ^TGLFont; - TGLFont = record - Font: TScalableFont; - X, Y, Z: real; - end; - -var - Fonts: array of TGLFont; - ActFont: integer; - -procedure BuildFont; // build our bitmap font -procedure KillFont; // delete the font -function glTextWidth(const text: string): real; // returns text width -procedure glPrint(const text: string); // custom GL "Print" routine -procedure ResetFont(); // reset font settings of active font -procedure SetFontPos(X, Y: real); // sets X and Y -procedure SetFontZ(Z: real); // sets Z -procedure SetFontSize(Size: real); -procedure SetFontStyle(Style: integer); // sets active font style (normal, bold, etc) -procedure SetFontItalic(Enable: boolean); // sets italic type letter (works for all fonts) -procedure SetFontReflection(Enable:boolean;Spacing: real); // enables/disables text reflection - -implementation - -uses - UMain, - UCommon, - UTextEncoding, - SysUtils, - IniFiles; - -function FindFontFile(FontIni: TCustomIniFile; Font: string): string; -var - Filename: string; -begin - Filename := FontIni.ReadString(Font, 'File', ''); - Result := FontPath + Filename; - // if path does not exist, try as an absolute path - if (not FileExists(Result)) then - Result := Filename; -end; - -procedure BuildFont; -var - FontIni: TMemIniFile; - //BitmapFont: TBitmapFont; - FontFile: string; -begin - ActFont := 0; - - SetLength(Fonts, 4); - FontIni := TMemIniFile.Create(FontPath + 'fontsTTF.ini'); - //FontIni := TMemIniFile.Create(FontPath + 'fonts.ini'); - - try - - // Normal - FontFile := FindFontFile(FontIni, 'Normal'); - Fonts[0].Font := TFTScalableFont.Create(FontFile, 64); - //Fonts[0].Font.GlyphSpacing := 1.4; - //Fonts[0].Font.Aspect := 1.2; - - { - BitmapFont := TBitmapFont.Create(FontFile, 0, 19, 35, -10); - BitmapFont.CorrectWidths(2, 0); - Fonts[0].Font := TScalableFont.Create(BitmapFont, false); - } - - //Fonts[0].Font.Aspect := 0.9; - - // Bold - FontFile := FindFontFile(FontIni, 'Bold'); - Fonts[1].Font := TFTScalableFont.Create(FontFile, 64); - - // Outline1 - FontFile := FindFontFile(FontIni, 'Outline1'); - Fonts[2].Font := TFTScalableOutlineFont.Create(FontFile, 64, 0.06); - //TFTScalableOutlineFont(Fonts[2].Font).SetOutlineColor(0.3, 0.3, 0.3); - - // Outline2 - FontFile := FindFontFile(FontIni, 'Outline2'); - Fonts[3].Font := TFTScalableOutlineFont.Create(FontFile, 64, 0.08); - - except on E: Exception do - Log.LogCritical(E.Message, 'BuildFont'); - end; - - // close ini-file - FontIni.Free; -end; - - -// Deletes the font -procedure KillFont; -begin - // delete all characters - //glDeleteLists(..., 256); -end; - -function glTextWidth(const text: string): real; -var - Bounds: TBoundsDbl; -begin - // FIXME: remove conversion - Bounds := Fonts[ActFont].Font.BBox(RecodeString(Text, encCP1252), true); - Result := Bounds.Right - Bounds.Left; -end; - -// Custom GL "Print" Routine -procedure glPrint(const Text: string); -var - GLFont: PGLFont; -begin - // if there is no text do nothing - if (Text = '') then - Exit; - - GLFont := @Fonts[ActFont]; - - glPushMatrix(); - // set font position - glTranslatef(GLFont.X, GLFont.Y + GLFont.Font.Ascender, GLFont.Z); - // draw string - // FIXME: remove conversion - GLFont.Font.Print(RecodeString(Text, encCP1252)); - glPopMatrix(); -end; - -procedure ResetFont(); -begin - SetFontPos(0, 0); - SetFontZ(0); - SetFontItalic(False); - SetFontReflection(False, 0); -end; - -procedure SetFontPos(X, Y: real); -begin - Fonts[ActFont].X := X; - Fonts[ActFont].Y := Y; -end; - -procedure SetFontZ(Z: real); -begin - Fonts[ActFont].Z := Z; -end; - -procedure SetFontSize(Size: real); -begin - Fonts[ActFont].Font.Height := Size; -end; - -procedure SetFontStyle(Style: integer); -begin - ActFont := Style; -end; - -procedure SetFontItalic(Enable: boolean); -begin - if (Enable) then - Fonts[ActFont].Font.Style := Fonts[ActFont].Font.Style + [Italic] - else - Fonts[ActFont].Font.Style := Fonts[ActFont].Font.Style - [Italic] -end; - -procedure SetFontReflection(Enable: boolean; Spacing: real); -begin - if (Enable) then - Fonts[ActFont].Font.Style := Fonts[ActFont].Font.Style + [Reflect] - else - Fonts[ActFont].Font.Style := Fonts[ActFont].Font.Style - [Reflect]; - Fonts[ActFont].Font.ReflectionSpacing := Spacing - Fonts[ActFont].Font.Descender; -end; - -end. diff --git a/Lua/src/base/UBeatTimer.pas b/Lua/src/base/UBeatTimer.pas new file mode 100644 index 00000000..310a49cd --- /dev/null +++ b/Lua/src/base/UBeatTimer.pas @@ -0,0 +1,170 @@ +{* 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 UBeatTimer; + +interface + +{$IFDEF FPC} + {$MODE Delphi} +{$ENDIF} + +{$I switches.inc} + +uses + UTime; + +type + (** + * TLyricsState contains all information concerning the + * state of the lyrics, e.g. the current beat or duration of the lyrics. + *) + TLyricsState = class + private + Timer: TRelativeTimer; // keeps track of the current time + public + OldBeat: integer; // previous discovered beat + CurrentBeat: integer; // current beat (rounded) + MidBeat: real; // current beat (float) + + // now we use this for super synchronization! + // only used when analyzing voice + // TODO: change ...D to ...Detect(ed) + OldBeatD: integer; // previous discovered beat + CurrentBeatD: integer; // current discovered beat (rounded) + MidBeatD: real; // current discovered beat (float) + + // we use this for audible clicks + // TODO: Change ...C to ...Click + OldBeatC: integer; // previous discovered beat + CurrentBeatC: integer; + MidBeatC: real; // like CurrentBeatC + + OldLine: integer; // previous displayed sentence + + StartTime: real; // time till start of lyrics (= Gap) + TotalTime: real; // total song time + + constructor Create(); + procedure Pause(); + procedure Resume(); + + procedure Reset(); + procedure UpdateBeats(); + + (** + * current song time (in seconds) used as base-timer for lyrics etc. + *) + function GetCurrentTime(): real; + procedure SetCurrentTime(Time: real); + end; + +implementation +uses UNote, Math; + + +constructor TLyricsState.Create(); +begin + // create a triggered timer, so we can Pause() it, set the time + // and Resume() it afterwards for better synching. + Timer := TRelativeTimer.Create(true); + + // reset state + Reset(); +end; + +procedure TLyricsState.Pause(); +begin + Timer.Pause(); +end; + +procedure TLyricsState.Resume(); +begin + Timer.Resume(); +end; + +procedure TLyricsState.SetCurrentTime(Time: real); +begin + // do not start the timer (if not started already), + // after setting the current time + Timer.SetTime(Time, false); +end; + +function TLyricsState.GetCurrentTime(): real; +begin + Result := Timer.GetTime(); +end; + +(** + * Resets the timer and state of the lyrics. + * The timer will be stopped afterwards so you have to call Resume() + * to start the lyrics timer. + *) +procedure TLyricsState.Reset(); +begin + Pause(); + SetCurrentTime(0); + + StartTime := 0; + TotalTime := 0; + + OldBeat := -1; + MidBeat := -1; + CurrentBeat := -1; + + OldBeatC := -1; + MidBeatC := -1; + CurrentBeatC := -1; + + OldBeatD := -1; + MidBeatD := -1; + CurrentBeatD := -1; +end; + +(** + * Updates the beat information (CurrentBeat/MidBeat/...) according to the + * current lyric time. + *) +procedure TLyricsState.UpdateBeats(); +var + CurLyricsTime: real; +begin + CurLyricsTime := GetCurrentTime(); + + OldBeat := CurrentBeat; + MidBeat := GetMidBeat(CurLyricsTime - StartTime / 1000); + CurrentBeat := Floor(MidBeat); + + OldBeatC := CurrentBeatC; + MidBeatC := GetMidBeat(CurLyricsTime - StartTime / 1000); + CurrentBeatC := Floor(MidBeatC); + + OldBeatD := CurrentBeatD; + // MidBeatD = MidBeat with additional GAP + MidBeatD := -0.5 + GetMidBeat(CurLyricsTime - (StartTime + 120 + 20) / 1000); + CurrentBeatD := Floor(MidBeatD); +end; + +end.
\ No newline at end of file diff --git a/Lua/src/base/UCatCovers.pas b/Lua/src/base/UCatCovers.pas index 4fc54199..d33bbbe1 100644 --- a/Lua/src/base/UCatCovers.pas +++ b/Lua/src/base/UCatCovers.pas @@ -38,20 +38,21 @@ interface {$I switches.inc} uses - UIni; + UIni, + UPath; type TCatCovers = class protected - cNames: array [0..high(ISorting)] of array of string; - cFiles: array [0..high(ISorting)] of array of string; + cNames: array [0..high(ISorting)] of array of UTF8String; + cFiles: array [0..high(ISorting)] of array of IPath; public constructor Create; procedure Load; //Load Cover aus Cover.ini and Cover Folder - procedure LoadPath(const CoversPath: string); - procedure Add(Sorting: integer; Name, Filename: string); //Add a Cover - function CoverExists(Sorting: integer; Name: string): boolean; //Returns True when a cover with the given Name exists - function GetCover(Sorting: integer; Name: string): string; //Returns the Filename of a Cover + procedure LoadPath(const CoversPath: IPath); + procedure Add(Sorting: integer; const Name: UTF8String; const Filename: IPath); //Add a Cover + function CoverExists(Sorting: integer; const Name: UTF8String): boolean; //Returns True when a cover with the given Name exists + function GetCover(Sorting: integer; const Name: UTF8String): IPath; //Returns the Filename of a Cover end; var @@ -63,9 +64,11 @@ uses IniFiles, SysUtils, Classes, - // UFiles, + UFilesystem, + ULog, UMain, - ULog; + UUnicodeUtils, + UPathUtils; constructor TCatCovers.Create; begin @@ -78,25 +81,28 @@ var I: integer; begin for I := 0 to CoverPaths.Count-1 do - LoadPath(CoverPaths[I]); + LoadPath(CoverPaths[I] as IPath); end; (** * Load Cover from Cover.ini and Cover Folder *) -procedure TCatCovers.LoadPath(const CoversPath: string); +procedure TCatCovers.LoadPath(const CoversPath: IPath); var Ini: TMemIniFile; - SR: TSearchRec; List: TStringlist; I, J: Integer; - Name, Filename, Temp: string; + Filename: IPath; + Name, TmpName: UTF8String; + CatCover: IPath; + Iter: IFileIterator; + FileInfo: TFileInfo; begin Ini := nil; List := nil; try - Ini := TMemIniFile.Create(CoversPath + 'covers.ini'); + Ini := TMemIniFile.Create(CoversPath.Append('covers.ini').ToNative); List := TStringlist.Create; //Add every Cover in Covers Ini for Every Sorting option @@ -105,63 +111,65 @@ begin Ini.ReadSection(ISorting[I], List); for J := 0 to List.Count - 1 do - Add(I, List.Strings[J], CoversPath + Ini.ReadString(ISorting[I], List.Strings[J], 'NoCover.jpg')); + begin + CatCover := Path(Ini.ReadString(ISorting[I], List.Strings[J], 'NoCover.jpg')); + Add(I, List.Strings[J], CoversPath.Append(CatCover)); + end; end; finally Ini.Free; List.Free; end; - try - //Add Covers from Folder - if (FindFirst (CoversPath + '*.jpg', faAnyFile, SR) = 0) then - repeat - //Add Cover if it doesn't exist for every Section - Name := SR.Name; - Filename := CoversPath + Name; - Delete (Name, length(Name) - 3, 4); - - for I := 0 to high(ISorting) do - begin - Temp := Name; - if ((I = sTitle) or (I = sTitle2)) and (Pos ('Title', Temp) <> 0) then - Delete (Temp, Pos ('Title', Temp), 5) - else if (I = sArtist) or (I = sArtist2) and (Pos ('Artist', Temp) <> 0) then - Delete (Temp, Pos ('Artist', Temp), 6); - - if not CoverExists(I, Temp) then - Add (I, Temp, Filename); - end; - until FindNext (SR) <> 0; - finally - FindClose (SR); + //Add Covers from Folder + Iter := FileSystem.FileFind(CoversPath.Append('*.jpg'), 0); + while Iter.HasNext do + begin + FileInfo := Iter.Next; + + //Add Cover if it doesn't exist for every Section + Filename := CoversPath.Append(FileInfo.Name); + Name := FileInfo.Name.SetExtension('').ToUTF8; + + for I := 0 to high(ISorting) do + begin + TmpName := Name; + if (I = sTitle) and (UTF8Pos('Title', TmpName) <> 0) then + UTF8Delete(TmpName, UTF8Pos('Title', TmpName), 5) + else if (I = sArtist) and (UTF8Pos('Artist', TmpName) <> 0) then + UTF8Delete(TmpName, UTF8Pos('Artist', TmpName), 6); + + if not CoverExists(I, TmpName) then + Add(I, TmpName, Filename); + end; end; end; //Add a Cover -procedure TCatCovers.Add(Sorting: integer; Name, Filename: string); +procedure TCatCovers.Add(Sorting: integer; const Name: UTF8String; const Filename: IPath); begin - if FileExists (Filename) then //If Exists -> Add + if Filename.IsFile then //If Exists -> Add begin - SetLength (CNames[Sorting], Length(CNames[Sorting]) + 1); - SetLength (CFiles[Sorting], Length(CNames[Sorting]) + 1); + SetLength(CNames[Sorting], Length(CNames[Sorting]) + 1); + SetLength(CFiles[Sorting], Length(CNames[Sorting]) + 1); - CNames[Sorting][high(cNames[Sorting])] := Uppercase(Name); + CNames[Sorting][high(cNames[Sorting])] := UTF8Uppercase(Name); CFiles[Sorting][high(cNames[Sorting])] := FileName; end; end; //Returns True when a cover with the given Name exists -function TCatCovers.CoverExists(Sorting: integer; Name: string): boolean; +function TCatCovers.CoverExists(Sorting: integer; const Name: UTF8String): boolean; var I: Integer; + UpperName: UTF8String; begin Result := False; - Name := Uppercase(Name); //Case Insensitiv + UpperName := UTF8Uppercase(Name); //Case Insensitiv for I := 0 to high(cNames[Sorting]) do begin - if (cNames[Sorting][I] = Name) then //Found Name + if (cNames[Sorting][I] = UpperName) then //Found Name begin Result := true; break; //Break For Loop @@ -170,16 +178,18 @@ begin end; //Returns the Filename of a Cover -function TCatCovers.GetCover(Sorting: integer; Name: string): string; +function TCatCovers.GetCover(Sorting: integer; const Name: UTF8String): IPath; var I: Integer; + UpperName: UTF8String; + NoCoverPath: IPath; begin - Result := ''; - Name := Uppercase(Name); + Result := PATH_NONE; + UpperName := UTF8Uppercase(Name); for I := 0 to high(cNames[Sorting]) do begin - if cNames[Sorting][I] = Name then + if cNames[Sorting][I] = UpperName then begin Result := cFiles[Sorting][I]; Break; @@ -187,13 +197,14 @@ begin end; //No Cover - if (Result = '') then + if (Result.IsUnset) then begin for I := 0 to CoverPaths.Count-1 do begin - if (FileExists(CoverPaths[I] + 'NoCover.jpg')) then + NoCoverPath := (CoverPaths[I] as IPath).Append('NoCover.jpg'); + if (NoCoverPath.IsFile) then begin - Result := CoverPaths[I] + 'NoCover.jpg'; + Result := NoCoverPath; Break; end; end; diff --git a/Lua/src/base/UCommandLine.pas b/Lua/src/base/UCommandLine.pas index 281a480d..ac0db2c2 100644 --- a/Lua/src/base/UCommandLine.pas +++ b/Lua/src/base/UCommandLine.pas @@ -33,6 +33,9 @@ interface {$I switches.inc} +uses + UPath; + type TScreenMode = (scmDefault, scmFullscreen, scmWindowed); @@ -64,9 +67,9 @@ type Screens: integer; // some strings set when reading infos {Length=0: Not Set} - SongPath: string; - ConfigFile: string; - ScoreFile: string; + SongPath: IPath; + ConfigFile: IPath; + ScoreFile: IPath; // pseudo integer values property Language: integer read GetLanguage; @@ -144,9 +147,9 @@ begin Screens := -1; // some strings set when reading infos {Length=0 Not Set} - SongPath := ''; - ConfigFile := ''; - ScoreFile := ''; + SongPath := PATH_NONE; + ConfigFile := PATH_NONE; + ScoreFile := PATH_NONE; end; {** @@ -248,7 +251,7 @@ begin if (PCount > I) then begin // write value to string - SongPath := ParamStr(I + 1); + SongPath := Path(ParamStr(I + 1)); end; end @@ -258,11 +261,11 @@ begin if (PCount > I) then begin // write value to string - ConfigFile := ParamStr(I + 1); + ConfigFile := Path(ParamStr(I + 1)); // is this a relative path -> then add gamepath - if Not ((Length(ConfigFile) > 2) AND (ConfigFile[2] = ':')) then - ConfigFile := ExtractFilePath(ParamStr(0)) + Configfile; + if (not ConfigFile.IsAbsolute) then + ConfigFile := Platform.GetExecutionDir().Append(ConfigFile); end; end @@ -272,7 +275,7 @@ begin if (PCount > I) then begin // write value to string - ScoreFile := ParamStr(I + 1); + ScoreFile := Path(ParamStr(I + 1)); end; end; diff --git a/Lua/src/base/UCommon.pas b/Lua/src/base/UCommon.pas index a52349c0..fa0faf3c 100644 --- a/Lua/src/base/UCommon.pas +++ b/Lua/src/base/UCommon.pas @@ -39,59 +39,54 @@ uses {$IFDEF MSWINDOWS} Windows, {$ENDIF} - sdl, UConfig, - ULog; + ULog, + UPath; type - TMessageType = ( mtInfo, mtError ); + TStringDynArray = array of string; -procedure ShowMessage( const msg : String; msgType: TMessageType = mtInfo ); +const + SepWhitespace = [#9, #10, #13, ' ']; // tab, lf, cr, space -procedure ConsoleWriteLn(const msg: string); +{** + * Splits a string into pieces separated by Separators. + * MaxCount specifies the max. number of pieces. If it is <= 0 the number is + * not limited. If > 0 the last array element will hold the rest of the string + * (with leading separators removed). + * + * Examples: + * SplitString(' split me now ', 0) -> ['split', 'me', 'now'] + * SplitString(' split me now ', 1) -> ['split', 'me now'] + *} +function SplitString(const Str: string; MaxCount: integer = 0; Separators: TSysCharSet = SepWhitespace): TStringDynArray; + + +type + TMessageType = (mtInfo, mtError); -function RWopsFromStream(Stream: TStream): PSDL_RWops; +procedure ShowMessage(const msg: string; msgType: TMessageType = mtInfo); + +procedure ConsoleWriteLn(const msg: string); {$IFDEF FPC} -function RandomRange(aMin: Integer; aMax: Integer) : Integer; +function RandomRange(aMin: integer; aMax: integer): integer; {$ENDIF} -function StringReplaceW(text : WideString; search, rep: WideChar):WideString; -function AdaptFilePaths( const aPath : widestring ): widestring; - procedure DisableFloatingPointExceptions(); procedure SetDefaultNumericLocale(); procedure RestoreNumericLocale(); {$IFNDEF MSWINDOWS} - procedure ZeroMemory( Destination: Pointer; Length: DWORD ); - function MakeLong(a, b: Word): Longint; - (* - #define LOBYTE(a) (BYTE)(a) - #define HIBYTE(a) (BYTE)((a)>>8) - #define LOWORD(a) (WORD)(a) - #define HIWORD(a) (WORD)((a)>>16) - #define MAKEWORD(a,b) (WORD)(((a)&0xff)|((b)<<8)) - *) +procedure ZeroMemory(Destination: pointer; Length: dword); +function MakeLong(a, b: word): longint; {$ENDIF} -function FileExistsInsensitive(var FileName: string): boolean; - -(* - * Character classes - *) - -function IsAlphaChar(ch: WideChar): boolean; -function IsNumericChar(ch: WideChar): boolean; -function IsAlphaNumericChar(ch: WideChar): boolean; -function IsPunctuationChar(ch: WideChar): boolean; -function IsControlChar(ch: WideChar): boolean; - // A stable alternative to TList.Sort() (use TList.Sort() if applicable, see below) procedure MergeSort(List: TList; CompareFunc: TListSortCompare); -function GetAlignedMem(Size: cardinal; Alignment: integer): Pointer; -procedure FreeAlignedMem(P: Pointer); +function GetAlignedMem(Size: cardinal; Alignment: integer): pointer; +procedure FreeAlignedMem(P: pointer); implementation @@ -101,8 +96,63 @@ uses {$IFDEF Delphi} Dialogs, {$ENDIF} - UMain; + sdl, + UFilesystem, + UMain, + UUnicodeUtils; + +function SplitString(const Str: string; MaxCount: integer; Separators: TSysCharSet): TStringDynArray; + {* + * Adds Str[StartPos..Endpos-1] to the result array. + *} + procedure AddSplit(StartPos, EndPos: integer); + begin + SetLength(Result, Length(Result)+1); + Result[High(Result)] := Copy(Str, StartPos, EndPos-StartPos); + end; + +var + I: integer; + Start: integer; + Last: integer; +begin + Start := 0; + SetLength(Result, 0); + + for I := 1 to Length(Str) do + begin + if (Str[I] in Separators) then + begin + // end of component found + if (Start > 0) then + begin + AddSplit(Start, I); + Start := 0; + end; + end + else if (Start = 0) then + begin + // mark beginning of component + Start := I; + // check if this is the last component + if (Length(Result) = MaxCount-1) then + begin + // find last non-separator char + Last := Length(Str); + while (Str[Last] in Separators) do + Dec(Last); + // add component up to last non-separator + AddSplit(Start, Last); + Exit; + end; + end; + end; + + // last component + if (Start > 0) then + AddSplit(Start, Length(Str)+1); +end; // data used by the ...Locale() functions {$IF Defined(Linux) or Defined(FreeBSD)} @@ -224,185 +274,23 @@ begin exOverflow, exUnderflow, exPrecision]); end; -function StringReplaceW(text : WideString; search, rep: WideChar) : WideString; -var - iPos : integer; -// sTemp : WideString; -begin -(* - result := text; - iPos := Pos(search, result); - while (iPos > 0) do - begin - sTemp := copy(result, iPos + length(search), length(result)); - result := copy(result, 1, iPos - 1) + rep + sTEmp; - iPos := Pos(search, result); - end; -*) - result := text; - - if search = rep then - exit; - - for iPos := 1 to length(result) do - begin - if result[iPos] = search then - result[iPos] := rep; - end; -end; - -function AdaptFilePaths( const aPath : widestring ): widestring; -begin - result := StringReplaceW( aPath, '\', PathDelim );//, [rfReplaceAll] ); -end; - - {$IFNDEF MSWINDOWS} -procedure ZeroMemory( Destination: Pointer; Length: DWORD ); +procedure ZeroMemory(Destination: pointer; Length: dword); begin - FillChar( Destination^, Length, 0 ); + FillChar(Destination^, Length, 0); end; -function MakeLong(A, B: Word): Longint; +function MakeLong(A, B: word): longint; begin Result := (LongInt(B) shl 16) + A; end; -(* -function QueryPerformanceCounter(lpPerformanceCount:TLARGEINTEGER):Bool; - - // From http://en.wikipedia.org/wiki/RDTSC - function RDTSC: Int64; register; - asm - rdtsc - end; - -begin - // Use clock_gettime(CLOCK_REALTIME, ...) here (but not from the libc unit) - lpPerformanceCount := RDTSC(); - result := true; -end; - -function QueryPerformanceFrequency(lpFrequency:TLARGEINTEGER):Bool; -begin - // clock_getres(CLOCK_REALTIME, ...) - lpFrequency := 0; - result := true; -end; -*) {$ENDIF} -// Checks if a regular files or directory with the given name exists. -// The comparison is case insensitive. -function FileExistsInsensitive(var FileName: string): boolean; -var - FilePath, LocalFileName: string; - SearchInfo: TSearchRec; -begin -{$IF Defined(Linux) or Defined(FreeBSD)} - // speed up standard case - if FileExists(FileName) then - begin - Result := true; - exit; - end; - - Result := false; - - FilePath := ExtractFilePath(FileName); - if (FindFirst(FilePath+'*', faAnyFile, SearchInfo) = 0) then - begin - LocalFileName := ExtractFileName(FileName); - repeat - if (AnsiSameText(LocalFileName, SearchInfo.Name)) then - begin - FileName := FilePath + SearchInfo.Name; - Result := true; - break; - end; - until (FindNext(SearchInfo) <> 0); - end; - FindClose(SearchInfo); -{$ELSE} - // Windows and Mac OS X do not have case sensitive file systems - Result := FileExists(FileName); -{$IFEND} -end; - -// +++++++++++++++++++++ helpers for RWOpsFromStream() +++++++++++++++ -function SdlStreamSeek( context : PSDL_RWops; offset : Integer; whence : Integer ) : integer; cdecl; -var - stream : TStream; - origin : Word; -begin - stream := TStream( context.unknown ); - if ( stream = nil ) then - raise EInvalidContainer.Create( 'SDLStreamSeek on nil' ); - case whence of - 0 : origin := soFromBeginning; // Offset is from the beginning of the resource. Seek moves to the position Offset. Offset must be >= 0. - 1 : origin := soFromCurrent; // Offset is from the current position in the resource. Seek moves to Position + Offset. - 2 : origin := soFromEnd; - else - origin := soFromBeginning; // just in case - end; - Result := stream.Seek( offset, origin ); -end; - -function SdlStreamRead( context : PSDL_RWops; Ptr : Pointer; size : Integer; maxnum: Integer ) : Integer; cdecl; -var - stream : TStream; -begin - stream := TStream( context.unknown ); - if ( stream = nil ) then - raise EInvalidContainer.Create( 'SDLStreamRead on nil' ); - try - Result := stream.read( Ptr^, Size * maxnum ) div size; - except - Result := -1; - end; -end; - -function SDLStreamClose( context : PSDL_RWops ) : Integer; cdecl; -var - stream : TStream; -begin - stream := TStream( context.unknown ); - if ( stream = nil ) then - raise EInvalidContainer.Create( 'SDLStreamClose on nil' ); - stream.Free; - Result := 1; -end; -// ----------------------------------------------- - -(* - * Creates an SDL_RWops handle from a TStream. - * The stream and RWops must be freed by the user after usage. - * Use SDL_FreeRW(...) to free the RWops data-struct. - *) -function RWopsFromStream(Stream: TStream): PSDL_RWops; -begin - Result := SDL_AllocRW(); - if (Result = nil) then - Exit; - - // set RW-callbacks - with Result^ do - begin - unknown := TUnknown(Stream); - seek := SDLStreamSeek; - read := SDLStreamRead; - write := nil; - close := SDLStreamClose; - type_ := 2; - end; -end; - - - {$IFDEF FPC} -function RandomRange(aMin: Integer; aMax: Integer) : Integer; +function RandomRange(aMin: integer; aMax: integer): integer; begin - RandomRange := Random(aMax-aMin) + aMin ; + RandomRange := Random(aMax - aMin) + aMin ; end; {$ENDIF} @@ -455,7 +343,7 @@ begin System.EnterCriticalSection(ConsoleCriticalSection); // output pending messages - for i := 0 to MessageList.Count-1 do + for i := 0 to MessageList.Count - 1 do begin _ConsoleWriteLn(MessageList[i]); end; @@ -528,7 +416,7 @@ end; procedure ShowMessage(const msg: String; msgType: TMessageType); {$IFDEF MSWINDOWS} -var Flags: Cardinal; +var Flags: cardinal; {$ENDIF} begin {$IF Defined(MSWINDOWS)} @@ -543,59 +431,6 @@ begin {$IFEND} end; -function IsAlphaChar(ch: WideChar): boolean; -begin - // TODO: add chars > 255 when unicode-fonts work? - case ch of - 'A'..'Z', // A-Z - 'a'..'z', // a-z - #170,#181,#186, - #192..#214, - #216..#246, - #248..#255: - Result := true; - else - Result := false; - end; -end; - -function IsNumericChar(ch: WideChar): boolean; -begin - case ch of - '0'..'9': - Result := true; - else - Result := false; - end; -end; - -function IsAlphaNumericChar(ch: WideChar): boolean; -begin - Result := (IsAlphaChar(ch) or IsNumericChar(ch)); -end; - -function IsPunctuationChar(ch: WideChar): boolean; -begin - // TODO: add chars outside of Latin1 basic (0..127)? - case ch of - ' '..'/',':'..'@','['..'`','{'..'~': - Result := true; - else - Result := false; - end; -end; - -function IsControlChar(ch: WideChar): boolean; -begin - case ch of - #0..#31, - #127..#159: - Result := true; - else - Result := false; - end; -end; - (* * Recursive part of the MergeSort algorithm. * OutList will be either InList or TempList and will be swapped in each @@ -607,7 +442,7 @@ procedure _MergeSort(InList, TempList, OutList: TList; StartPos, BlockSize: inte CompareFunc: TListSortCompare); var LeftSize, RightSize: integer; // number of elements in left/right block - LeftEnd, RightEnd: integer; // Index after last element in left/right block + LeftEnd, RightEnd: integer; // Index after last element in left/right block MidPos: integer; // index of first element in right block Pos: integer; // position in output list begin @@ -683,7 +518,7 @@ end; type // stores the unaligned pointer of data allocated by GetAlignedMem() PMemAlignHeader = ^TMemAlignHeader; - TMemAlignHeader = Pointer; + TMemAlignHeader = pointer; (** * Use this function to assure that allocated memory is aligned on a specific @@ -699,9 +534,9 @@ type * alignments on 16 and 32 byte boundaries too. *) {$WARNINGS OFF} -function GetAlignedMem(Size: cardinal; Alignment: integer): Pointer; +function GetAlignedMem(Size: cardinal; Alignment: integer): pointer; var - OrigPtr: Pointer; + OrigPtr: pointer; const MIN_ALIGNMENT = 16; begin @@ -722,9 +557,9 @@ begin end; // reserve space for the header - Result := Pointer(PtrUInt(OrigPtr) + SizeOf(TMemAlignHeader)); + Result := pointer(PtrUInt(OrigPtr) + SizeOf(TMemAlignHeader)); // align memory - Result := Pointer(PtrUInt(Result) + Alignment - PtrUInt(Result) mod Alignment); + Result := pointer(PtrUInt(Result) + Alignment - PtrUInt(Result) mod Alignment); // set header with info on old pointer for FreeMem PMemAlignHeader(PtrUInt(Result) - SizeOf(TMemAlignHeader))^ := OrigPtr; @@ -732,7 +567,7 @@ end; {$WARNINGS ON} {$WARNINGS OFF} -procedure FreeAlignedMem(P: Pointer); +procedure FreeAlignedMem(P: pointer); begin if (P <> nil) then FreeMem(PMemAlignHeader(PtrUInt(P) - SizeOf(TMemAlignHeader))^); diff --git a/Lua/src/base/UConfig.pas b/Lua/src/base/UConfig.pas index 773d6203..a24242e8 100644 --- a/Lua/src/base/UConfig.pas +++ b/Lua/src/base/UConfig.pas @@ -90,7 +90,7 @@ interface {$I switches.inc} uses - Sysutils; + SysUtils; const // IMPORTANT: @@ -156,6 +156,12 @@ const (FPC_RELEASE * VERSION_MINOR) + (FPC_PATCH * VERSION_RELEASE); + // FPC 2.2.0 unicode support is very buggy. The cwstring unit for example + // always crashes whenever UTF8ToAnsi() is called on a non UTF8 encoded string + // what is fixed in 2.2.2. + {$IF Defined(FPC) and (FPC_VERSION_INT < 2002002)} // < 2.2.2 + {$MESSAGE FATAL 'FPC >= 2.2.2 required!'} + {$IFEND} {$IFDEF HaveFFmpeg} diff --git a/Lua/src/base/UCore.pas b/Lua/src/base/UCore.pas deleted file mode 100644 index 901f2f96..00000000 --- a/Lua/src/base/UCore.pas +++ /dev/null @@ -1,550 +0,0 @@ -{* 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 UCore; - -interface - -{$IFDEF FPC} - {$MODE Delphi} -{$ENDIF} - -{$I switches.inc} - -uses - uPluginDefs, - uCoreModule, - UHooks, - UServices, - UModules; - -{********************* - TCore - Class manages all CoreModules, teh StartUp, teh MainLoop and the shutdown process - Also it does some Error Handling, and maybe sometime multithreaded Loading ;) -*********************} - -type - TModuleListItem = record - Module: TCoreModule; //Instance of the Modules Class - Info: TModuleInfo; //ModuleInfo returned by Modules Modulinfo Proc - NeedsDeInit: Boolean; //True if Module was succesful inited - end; - - TCore = class - private - //Some Hook Handles. See Plugin SDKs Hooks.txt for Infos - hLoadingFinished: THandle; - hMainLoop: THandle; - hTranslate: THandle; - hLoadTextures: THandle; - hExitQuery: THandle; - hExit: THandle; - hDebug: THandle; - hError: THandle; - sReportError: THandle; - sReportDebug: THandle; - sShowMessage: THandle; - sRetranslate: THandle; - sReloadTextures: THandle; - sGetModuleInfo: THandle; - sGetApplicationHandle: THandle; - - Modules: Array [0..High(CORE_MODULES_TO_LOAD)] of TModuleListItem; - - //Cur + Last Executed Setting and Getting ;) - iCurExecuted: Integer; - iLastExecuted: Integer; - - procedure SetCurExecuted(Value: Integer); - - //Function Get all Modules and Creates them - function GetModules: Boolean; - - //Loads Core and all Modules - function Load: Boolean; - - //Inits Core and all Modules - function Init: Boolean; - - //DeInits Core and all Modules - function DeInit: Boolean; - - //Load the Core - function LoadCore: Boolean; - - //Init the Core - function InitCore: Boolean; - - //DeInit the Core - function DeInitCore: Boolean; - - //Called one Time per Frame - function MainLoop: Boolean; - - public - Hooks: THookManager; //Teh Hook Manager ;) - Services: TServiceManager;//The Service Manager - - Name: String; //Name of this Application - Version: LongWord; //Version of this ". For Info Look PluginDefs Functions - - LastErrorReporter:String; //Who Reported the Last Error String - LastErrorString: String; //Last Error String reported - - property CurExecuted: Integer read iCurExecuted write SetCurExecuted; //ID of Plugin or Module curently Executed - property LastExecuted: Integer read iLastExecuted; - - //--------------- - //Main Methods to control the Core: - //--------------- - constructor Create(const cName: String; const cVersion: LongWord); - - //Starts Loading and Init Process. Then Runs MainLoop. DeInits on Shutdown - procedure Run; - - //Method for other Classes to get Pointer to a specific Module - function GetModulebyName(const Name: String): PCoreModule; - - //-------------- - // Hook and Service Procs: - //-------------- - function ShowMessage(wParam: TwParam; lParam: TlParam): integer; //Shows a Message (lParam: PChar Text, wParam: Symbol) - function ReportError(wParam: TwParam; lParam: TlParam): integer; //Shows a Message (wParam: Pchar(Message), lParam: PChar(Reportername)) - function ReportDebug(wParam: TwParam; lParam: TlParam): integer; //Shows a Message (wParam: Pchar(Message), lParam: PChar(Reportername)) - function Retranslate(wParam: TwParam; lParam: TlParam): integer; //Calls Translate hook - function ReloadTextures(wParam: TwParam; lParam: TlParam): integer; //Calls LoadTextures hook - function GetModuleInfo(wParam: TwParam; lParam: TlParam): integer; //If lParam = nil then get length of Moduleinfo Array. If lparam <> nil then write array of TModuleInfo to address at lparam - function GetApplicationHandle(wParam: TwParam; lParam: TlParam): integer; //Returns Application Handle - end; - -var - Core: TCore; - -implementation - -uses - {$IFDEF win32} - Windows, - {$ENDIF} - SysUtils; - -//------------- -// Create - Creates Class + Hook and Service Manager -//------------- -constructor TCore.Create(const cName: String; const cVersion: LongWord); -begin - inherited Create; - - Name := cName; - Version := cVersion; - iLastExecuted := 0; - iCurExecuted := 0; - - LastErrorReporter := ''; - LastErrorString := ''; - - Hooks := THookManager.Create(50); - Services := TServiceManager.Create; -end; - -//------------- -//Starts Loading and Init Process. Then Runs MainLoop. DeInits on Shutdown -//------------- -procedure TCore.Run; -var - Success: Boolean; - - procedure HandleError(const ErrorMsg: string); - begin - if (LastErrorString <> '') then - Self.ShowMessage(CORE_SM_ERROR, PChar(ErrorMsg + ': ' + LastErrorString)) - else - Self.ShowMessage(CORE_SM_ERROR, PChar(ErrorMsg)); - - //DeInit - DeInit; - end; - -begin - //Get Modules - try - Success := GetModules(); - except - Success := False; - end; - - if (not Success) then - begin - HandleError('Error Getting Modules'); - Exit; - end; - - //Loading - try - Success := Load(); - except - Success := False; - end; - - if (not Success) then - begin - HandleError('Error loading Modules'); - Exit; - end; - - //Init - try - Success := Init(); - except - Success := False; - end; - - if (not Success) then - begin - HandleError('Error initing Modules'); - Exit; - end; - - //Call Translate Hook - if (Hooks.CallEventChain(hTranslate, 0, nil) <> 0) then - begin - HandleError('Error translating'); - Exit; - end; - - //Calls LoadTextures Hook - if (Hooks.CallEventChain(hLoadTextures, 0, nil) <> 0) then - begin - HandleError('Error loading textures'); - Exit; - end; - - //Calls Loading Finished Hook - if (Hooks.CallEventChain(hLoadingFinished, 0, nil) <> 0) then - begin - HandleError('Error calling LoadingFinished Hook'); - Exit; - end; - - //Start MainLoop - while Success do - begin - Success := MainLoop(); - // to-do : Call Display Draw here - end; -end; - -//------------- -//Called one Time per Frame -//------------- -function TCore.MainLoop: Boolean; -begin - Result := False; -end; - -//------------- -//Function Get all Modules and Creates them -//------------- -function TCore.GetModules: Boolean; -var - i: Integer; -begin - Result := False; - for i := 0 to high(Modules) do - begin - try - Modules[i].NeedsDeInit := False; - Modules[i].Module := CORE_MODULES_TO_LOAD[i].Create; - Modules[i].Module.Info(@Modules[i].Info); - except - ReportError(Integer(PChar('Can''t get module #' + InttoStr(i) + ' "' + Modules[i].Info.Name + '"')), PChar('Core')); - Exit; - end; - end; - Result := True; -end; - -//------------- -//Loads Core and all Modules -//------------- -function TCore.Load: Boolean; -var - i: Integer; -begin - Result := LoadCore; - - for i := 0 to High(CORE_MODULES_TO_LOAD) do - begin - try - Result := Modules[i].Module.Load; - except - Result := False; - end; - - if (not Result) then - begin - ReportError(Integer(PChar('Error loading module #' + InttoStr(i) + ' "' + Modules[i].Info.Name + '"')), PChar('Core')); - break; - end; - end; -end; - -//------------- -//Inits Core and all Modules -//------------- -function TCore.Init: Boolean; -var - i: Integer; -begin - Result := InitCore; - - for i := 0 to High(CORE_MODULES_TO_LOAD) do - begin - try - Result := Modules[i].Module.Init; - except - Result := False; - end; - - if (not Result) then - begin - ReportError(Integer(PChar('Error initing module #' + InttoStr(i) + ' "' + Modules[i].Info.Name + '"')), PChar('Core')); - break; - end; - - Modules[i].NeedsDeInit := Result; - end; -end; - -//------------- -//DeInits Core and all Modules -//------------- -function TCore.DeInit: boolean; -var - i: integer; -begin - - for i := High(CORE_MODULES_TO_LOAD) downto 0 do - begin - try - if (Modules[i].NeedsDeInit) then - Modules[i].Module.DeInit; - except - end; - end; - - DeInitCore; - - Result := true; -end; - -//------------- -//Load the Core -//------------- -function TCore.LoadCore: Boolean; -begin - hLoadingFinished := Hooks.AddEvent('Core/LoadingFinished'); - hMainLoop := Hooks.AddEvent('Core/MainLoop'); - hTranslate := Hooks.AddEvent('Core/Translate'); - hLoadTextures := Hooks.AddEvent('Core/LoadTextures'); - hExitQuery := Hooks.AddEvent('Core/ExitQuery'); - hExit := Hooks.AddEvent('Core/Exit'); - hDebug := Hooks.AddEvent('Core/NewDebugInfo'); - hError := Hooks.AddEvent('Core/NewError'); - - sReportError := Services.AddService('Core/ReportError', nil, Self.ReportError); - sReportDebug := Services.AddService('Core/ReportDebug', nil, Self.ReportDebug); - sShowMessage := Services.AddService('Core/ShowMessage', nil, Self.ShowMessage); - sRetranslate := Services.AddService('Core/Retranslate', nil, Self.Retranslate); - sReloadTextures := Services.AddService('Core/ReloadTextures', nil, Self.ReloadTextures); - sGetModuleInfo := Services.AddService('Core/GetModuleInfo', nil, Self.GetModuleInfo); - sGetApplicationHandle := Services.AddService('Core/GetApplicationHandle', nil, Self.GetApplicationHandle); - - //A little Test - Hooks.AddSubscriber('Core/NewError', HookTest); - - result := true; -end; - -//------------- -//Init the Core -//------------- -function TCore.InitCore: Boolean; -begin - //Don not init something atm. - result := true; -end; - -//------------- -//DeInit the Core -//------------- -function TCore.DeInitCore: Boolean; -begin - // TODO: write TService-/HookManager.Free and call it here - Result := true; -end; - -//------------- -//Method for other classes to get pointer to a specific module -//------------- -function TCore.GetModuleByName(const Name: String): PCoreModule; -var i: Integer; -begin - Result := nil; - for i := 0 to High(Modules) do - begin - if (Modules[i].Info.Name = Name) then - begin - Result := @Modules[i].Module; - Break; - end; - end; -end; - -//------------- -// Shows a MessageDialog (lParam: PChar Text, wParam: Symbol) -//------------- -function TCore.ShowMessage(wParam: TwParam; lParam: TlParam): integer; -{$IFDEF MSWINDOWS} -var Params: Cardinal; -{$ENDIF} -begin - Result := -1; - - {$IFDEF MSWINDOWS} - if (lParam <> nil) then - begin - Params := MB_OK; - case wParam of - CORE_SM_ERROR: Params := Params or MB_ICONERROR; - CORE_SM_WARNING: Params := Params or MB_ICONWARNING; - CORE_SM_INFO: Params := Params or MB_ICONINFORMATION; - end; - - //Show: - Result := Messagebox(0, lParam, PChar(Name), Params); - end; - {$ENDIF} - - // TODO: write ShowMessage for other OSes -end; - -//------------- -// Calls NewError HookChain (wParam: Pchar(Message), lParam: PChar(Reportername)) -//------------- -function TCore.ReportError(wParam: TwParam; lParam: TlParam): integer; -begin - //Update LastErrorReporter and LastErrorString - LastErrorReporter := String(PChar(lParam)); - LastErrorString := String(PChar(Pointer(wParam))); - - Hooks.CallEventChain(hError, wParam, lParam); - - // FIXME: return a correct result - Result := 0; -end; - -//------------- -// Calls NewDebugInfo HookChain (wParam: Pchar(Message), lParam: PChar(Reportername)) -//------------- -function TCore.ReportDebug(wParam: TwParam; lParam: TlParam): integer; -begin - Hooks.CallEventChain(hDebug, wParam, lParam); - - // FIXME: return a correct result - Result := 0; -end; - -//------------- -// Calls Translate hook -//------------- -function TCore.Retranslate(wParam: TwParam; lParam: TlParam): integer; -begin - Hooks.CallEventChain(hTranslate, 1, nil); - - // FIXME: return a correct result - Result := 0; -end; - -//------------- -// Calls LoadTextures hook -//------------- -function TCore.ReloadTextures(wParam: TwParam; lParam: TlParam): integer; -begin - Hooks.CallEventChain(hLoadTextures, 1, nil); - - // FIXME: return a correct result - Result := 0; -end; - -//------------- -// If lParam = nil then get length of Moduleinfo Array. If lparam <> nil then write array of TModuleInfo to address at lparam -//------------- -function TCore.GetModuleInfo(wParam: TwParam; lParam: TlParam): integer; -var - I: integer; -begin - if (Pointer(lParam) = nil) then - begin - Result := Length(Modules); - end - else - begin - try - for I := 0 to High(Modules) do - begin - AModuleInfo(Pointer(lParam))[I].Name := Modules[I].Info.Name; - AModuleInfo(Pointer(lParam))[I].Version := Modules[I].Info.Version; - AModuleInfo(Pointer(lParam))[I].Description := Modules[I].Info.Description; - end; - Result := Length(Modules); - except - Result := -1; - end; - end; -end; - -//------------- -// Returns Application Handle -//------------- -function TCore.GetApplicationHandle(wParam: TwParam; lParam: TlParam): integer; -begin - Result := hInstance; -end; - -//------------- -// Called when setting CurExecuted -//------------- -procedure TCore.SetCurExecuted(Value: Integer); -begin - //Set Last Executed - iLastExecuted := iCurExecuted; - - //Set Cur Executed - iCurExecuted := Value; -end; - -end. diff --git a/Lua/src/base/UCoreModule.pas b/Lua/src/base/UCoreModule.pas deleted file mode 100644 index b87fec85..00000000 --- a/Lua/src/base/UCoreModule.pas +++ /dev/null @@ -1,154 +0,0 @@ -{* 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 UCoreModule; - -interface - -{$IFDEF FPC} - {$MODE Delphi} -{$ENDIF} - -{$I switches.inc} - -{********************* - TCoreModule - Dummy class that has methods that will be called from core - In the best case every piece of this software is a module -*********************} -uses - UPluginDefs; - -type - PCoreModule = ^TCoreModule; - TCoreModule = class - public - Constructor Create; virtual; - - //Function that gives some Infos about the Module to the Core - Procedure Info(const pInfo: PModuleInfo); virtual; - - //Is Called on Loading. - //In this Method only Events and Services should be created - //to offer them to other Modules or Plugins during the Init process - //If False is Returned this will cause a Forced Exit - Function Load: Boolean; virtual; - - //Is Called on Init Process - //In this Method you can Hook some Events and Create + Init - //your Classes, Variables etc. - //If False is Returned this will cause a Forced Exit - Function Init: Boolean; virtual; - - //Is Called during Mainloop before 'Core/MainLoop' Hook and Drawing - //If False is Returned this will cause a Forced Exit - Function MainLoop: Boolean; virtual; - - //Is Called if this Module has been Inited and there is a Exit. - //Deinit is in backwards Initing Order - //If False is Returned this will cause a Forced Exit - Procedure DeInit; virtual; - - //Is Called if this Module will be unloaded and has been created - //Should be used to Free Memory - Destructor Destroy; override; - end; - cCoreModule = class of TCoreModule; - -implementation - -//------------- -// Just the Constructor -//------------- -Constructor TCoreModule.Create; -begin - //Dummy maaaan ;) - inherited; -end; - -//------------- -// Function that gives some Infos about the Module to the Core -//------------- -Procedure TCoreModule.Info(const pInfo: PModuleInfo); -begin - pInfo^.Name := 'Not Set'; - pInfo^.Version := 0; - pInfo^.Description := 'Not Set'; -end; - -//------------- -//Is Called on Loading. -//In this Method only Events and Services should be created -//to offer them to other Modules or Plugins during the Init process -//If False is Returned this will cause a Forced Exit -//------------- -Function TCoreModule.Load: Boolean; -begin - //Dummy ftw!! - Result := True; -end; - -//------------- -//Is Called on Init Process -//In this Method you can Hook some Events and Create + Init -//your Classes, Variables etc. -//If False is Returned this will cause a Forced Exit -//------------- -Function TCoreModule.Init: Boolean; -begin - //Dummy ftw!! - Result := True; -end; - -//------------- -//Is Called during Mainloop before 'Core/MainLoop' Hook and Drawing -//If False is Returned this will cause a Forced Exit -//------------- -Function TCoreModule.MainLoop: Boolean; -begin - //Dummy ftw!! - Result := True; -end; - -//------------- -//Is Called if this Module has been Inited and there is a Exit. -//Deinit is in backwards Initing Order -//------------- -Procedure TCoreModule.DeInit; -begin - //Dummy ftw!! -end; - -//------------- -//Is Called if this Module will be unloaded and has been created -//Should be used to Free Memory -//------------- -Destructor TCoreModule.Destroy; -begin - //Dummy ftw!! - inherited; -end; - -end. diff --git a/Lua/src/base/UCovers.pas b/Lua/src/base/UCovers.pas index a1705674..6c7c9e48 100644 --- a/Lua/src/base/UCovers.pas +++ b/Lua/src/base/UCovers.pas @@ -50,7 +50,8 @@ uses SysUtils, Classes, UImage, - UTexture; + UTexture, + UPath; type ECoverDBException = class(Exception) @@ -59,9 +60,9 @@ type TCover = class private ID: int64; - Filename: WideString; + Filename: IPath; public - constructor Create(ID: int64; Filename: WideString); + constructor Create(ID: int64; Filename: IPath); function GetPreviewTexture(): TTexture; function GetTexture(): TTexture; end; @@ -76,19 +77,19 @@ type private DB: TSQLiteDatabase; procedure InitCoverDatabase(); - function CreateThumbnail(const Filename: WideString; var Info: TThumbnailInfo): PSDL_Surface; + function CreateThumbnail(const Filename: IPath; var Info: TThumbnailInfo): PSDL_Surface; function LoadCover(CoverID: int64): TTexture; procedure DeleteCover(CoverID: int64); - function FindCoverIntern(const Filename: WideString): int64; + function FindCoverIntern(const Filename: IPath): int64; procedure Open(); function GetVersion(): integer; procedure SetVersion(Version: integer); public constructor Create(); destructor Destroy; override; - function AddCover(const Filename: WideString): TCover; - function FindCover(const Filename: WideString): TCover; - function CoverExists(const Filename: WideString): boolean; + function AddCover(const Filename: IPath): TCover; + function FindCover(const Filename: IPath): TCover; + function CoverExists(const Filename: IPath): boolean; function GetMaxCoverSize(): integer; procedure SetMaxCoverSize(Size: integer); end; @@ -111,7 +112,7 @@ uses DateUtils; const - COVERDB_FILENAME = 'cover.db'; + COVERDB_FILENAME: UTF8String = 'cover.db'; COVERDB_VERSION = 01; // 0.1 COVER_TBL = 'Cover'; COVER_THUMBNAIL_TBL = 'CoverThumbnail'; @@ -141,7 +142,7 @@ end; { TCover } -constructor TCover.Create(ID: int64; Filename: WideString); +constructor TCover.Create(ID: int64; Filename: IPath); begin Self.ID := ID; Self.Filename := Filename; @@ -210,11 +211,11 @@ end; procedure TCoverDatabase.Open(); var Version: integer; - Filename: string; + Filename: IPath; begin - Filename := UTF8Encode(Platform.GetGameUserPath() + COVERDB_FILENAME); + Filename := Platform.GetGameUserPath().Append(COVERDB_FILENAME); - DB := TSQLiteDatabase.Create(Filename); + DB := TSQLiteDatabase.Create(Filename.ToUTF8()); Version := GetVersion(); // check version, if version is too old/new, delete database file @@ -223,10 +224,10 @@ begin Log.LogInfo('Outdated cover-database file found', 'TCoverDatabase.Open'); // close and delete outdated file DB.Free; - if (not DeleteFile(Filename)) then - raise ECoverDBException.Create('Could not delete ' + Filename); + if (not Filename.DeleteFile()) then + raise ECoverDBException.Create('Could not delete ' + Filename.ToNative); // reopen - DB := TSQLiteDatabase.Create(Filename); + DB := TSQLiteDatabase.Create(Filename.ToUTF8()); Version := 0; end; @@ -266,14 +267,14 @@ begin ')'); end; -function TCoverDatabase.FindCoverIntern(const Filename: WideString): int64; +function TCoverDatabase.FindCoverIntern(const Filename: IPath): int64; begin Result := DB.GetTableValue('SELECT [ID] FROM ['+COVER_TBL+'] ' + 'WHERE [Filename] = ?', - [UTF8Encode(Filename)]); + [Filename.ToUTF8]); end; -function TCoverDatabase.FindCover(const Filename: WideString): TCover; +function TCoverDatabase.FindCover(const Filename: IPath): TCover; var CoverID: int64; begin @@ -287,7 +288,7 @@ begin end; end; -function TCoverDatabase.CoverExists(const Filename: WideString): boolean; +function TCoverDatabase.CoverExists(const Filename: IPath): boolean; begin Result := false; try @@ -297,7 +298,7 @@ begin end; end; -function TCoverDatabase.AddCover(const Filename: WideString): TCover; +function TCoverDatabase.AddCover(const Filename: IPath): TCover; var CoverID: int64; Thumbnail: PSDL_Surface; @@ -329,7 +330,7 @@ begin DB.ExecSQL('INSERT INTO ['+COVER_TBL+'] ' + '([Filename], [Date], [Width], [Height]) VALUES' + '(?, ?, ?, ?)', - [UTF8Encode(Filename), DateTimeToUnixTime(FileDate), + [Filename.ToUTF8, DateTimeToUnixTime(FileDate), Info.CoverWidth, Info.CoverHeight]); // get auto-generated cover ID @@ -358,7 +359,7 @@ var PixelFmt: TImagePixelFmt; Data: PChar; DataSize: integer; - Filename: WideString; + Filename: IPath; Table: TSQLiteUniTable; begin Table := nil; @@ -371,7 +372,7 @@ begin 'USING(ID) ' + 'WHERE [ID] = %d', [CoverID])); - Filename := UTF8Decode(Table.FieldAsString(0)); + Filename := Path(Table.FieldAsString(0)); PixelFmt := TImagePixelFmt(Table.FieldAsInteger(1)); Width := Table.FieldAsInteger(2); Height := Table.FieldAsInteger(3); @@ -384,6 +385,9 @@ begin end else begin + // FillChar() does not decrement the ref-count of ref-counted fields + // -> reset Name field manually + Result.Name := nil; FillChar(Result, SizeOf(TTexture), 0); end; except on E: Exception do @@ -403,7 +407,7 @@ end; * Returns a pointer to an array of bytes containing the texture data in the * requested size *) -function TCoverDatabase.CreateThumbnail(const Filename: WideString; var Info: TThumbnailInfo): PSDL_Surface; +function TCoverDatabase.CreateThumbnail(const Filename: IPath; var Info: TThumbnailInfo): PSDL_Surface; var //TargetAspect, SourceAspect: double; //TargetWidth, TargetHeight: integer; @@ -417,7 +421,7 @@ begin Thumbnail := LoadImage(Filename); if (not assigned(Thumbnail)) then begin - Log.LogError('Could not load cover: "'+ Filename +'"', 'TCoverDatabase.AddCover'); + Log.LogError('Could not load cover: "'+ Filename.ToNative +'"', 'TCoverDatabase.AddCover'); Exit; end; diff --git a/Lua/src/base/UDLLManager.pas b/Lua/src/base/UDLLManager.pas index cd4b7991..d5bb1480 100644 --- a/Lua/src/base/UDLLManager.pas +++ b/Lua/src/base/UDLLManager.pas @@ -35,42 +35,49 @@ interface uses ModiSDK, - UFiles; + UFiles, + UPath, + UFilesystem; type TDLLMan = class private - hLib: THandle; + hLib: THandle; P_Init: fModi_Init; P_Draw: fModi_Draw; P_Finish: fModi_Finish; P_RData: pModi_RData; public Plugins: array of TPluginInfo; - PluginPaths: array of String; + PluginPaths: array of IPath; Selected: ^TPluginInfo; constructor Create; procedure GetPluginList; - procedure ClearPluginInfo(No: Cardinal); - function LoadPluginInfo(Filename: String; No: Cardinal): boolean; + procedure ClearPluginInfo(No: cardinal); + function LoadPluginInfo(const Filename: IPath; No: cardinal): boolean; - function LoadPlugin(No: Cardinal): boolean; + function LoadPlugin(No: cardinal): boolean; procedure UnLoadPlugin; - function PluginInit (const TeamInfo: TTeamInfo; var Playerinfo: TPlayerinfo; const Sentences: TSentences; const LoadTex: fModi_LoadTex; const Print: fModi_Print; LoadSound: fModi_LoadSound; PlaySound: pModi_PlaySound): boolean; - function PluginDraw (var Playerinfo: TPlayerinfo; const CurSentence: Cardinal): boolean; + function PluginInit (const TeamInfo: TTeamInfo; + var Playerinfo: TPlayerinfo; + const Sentences: TSentences; + const LoadTex: fModi_LoadTex; + const Print: fModi_Print; + LoadSound: fModi_LoadSound; + PlaySound: pModi_PlaySound) + : boolean; + function PluginDraw (var Playerinfo: TPlayerinfo; const CurSentence: cardinal): boolean; function PluginFinish (var Playerinfo: TPlayerinfo): byte; - procedure PluginRData (handle: HSTREAM; buffer: Pointer; len: DWORD; user: DWORD); + procedure PluginRData (handle: HSTREAM; buffer: Pointer; len: dword; user: dword); end; var DLLMan: TDLLMan; const - DLLPath = 'Plugins'; - {$IF Defined(MSWINDOWS)} DLLExt = '.dll'; {$ELSEIF Defined(DARWIN)} @@ -87,6 +94,7 @@ uses {$ELSE} dynlibs, {$ENDIF} + UPathUtils, ULog, SysUtils; @@ -101,33 +109,32 @@ end; procedure TDLLMan.GetPluginList; var - SR: TSearchRec; + Iter: IFileIterator; + FileInfo: TFileInfo; begin - - if FindFirst(DLLPath +PathDelim+ '*' + DLLExt, faAnyFile , SR) = 0 then + Iter := FileSystem.FileFind(PluginPath.Append('*' + DLLExt), 0); + while (Iter.HasNext) do begin - repeat - SetLength(Plugins, Length(Plugins)+1); - SetLength(PluginPaths, Length(Plugins)); + SetLength(Plugins, Length(Plugins)+1); + SetLength(PluginPaths, Length(Plugins)); - if LoadPluginInfo(SR.Name, High(Plugins)) then //Loaded succesful - begin - PluginPaths[High(PluginPaths)] := SR.Name; - end - else //Error Loading - begin - SetLength(Plugins, Length(Plugins)-1); - SetLength(PluginPaths, Length(Plugins)); - end; - - until FindNext(SR) <> 0; - FindClose(SR); + FileInfo := Iter.Next; + + if LoadPluginInfo(FileInfo.Name, High(Plugins)) then // loaded succesful + begin + PluginPaths[High(PluginPaths)] := FileInfo.Name; + end + else // error loading + begin + SetLength(Plugins, Length(Plugins)-1); + SetLength(PluginPaths, Length(Plugins)); + end; end; end; -procedure TDLLMan.ClearPluginInfo(No: Cardinal); +procedure TDLLMan.ClearPluginInfo(No: cardinal); begin - //Set to Party Modi Plugin +// set to party modi plugin Plugins[No].Typ := 8; Plugins[No].Name := 'unknown'; @@ -136,109 +143,117 @@ begin Plugins[No].Creator := 'Nobody'; Plugins[No].PluginDesc := 'NO_PLUGIN_DESC'; - Plugins[No].LoadSong := True; - Plugins[No].ShowScore := True; - Plugins[No].ShowBars := False; - Plugins[No].ShowNotes := True; - Plugins[No].LoadVideo := True; - Plugins[No].LoadBack := True; + Plugins[No].LoadSong := true; + Plugins[No].ShowScore := true; + Plugins[No].ShowBars := true; + Plugins[No].ShowNotes := true; + Plugins[No].LoadVideo := true; + Plugins[No].LoadBack := true; - Plugins[No].TeamModeOnly := False; - Plugins[No].GetSoundData := False; - Plugins[No].Dummy := False; + Plugins[No].TeamModeOnly := true; + Plugins[No].GetSoundData := true; + Plugins[No].Dummy := true; - Plugins[No].BGShowFull := False; - Plugins[No].BGShowFull_O := True; + Plugins[No].BGShowFull := true; + Plugins[No].BGShowFull_O := true; - Plugins[No].ShowRateBar:= False; - Plugins[No].ShowRateBar_O := True; + Plugins[No].ShowRateBar := true; + Plugins[No].ShowRateBar_O := true; - Plugins[No].EnLineBonus := False; - Plugins[No].EnLineBonus_O := True; + Plugins[No].EnLineBonus := true; + Plugins[No].EnLineBonus_O := true; end; -function TDLLMan.LoadPluginInfo(Filename: String; No: Cardinal): boolean; +function TDLLMan.LoadPluginInfo(const Filename: IPath; No: cardinal): boolean; var hLibg: THandle; Info: pModi_PluginInfo; - //I: Integer; +// I: integer; begin - Result := False; - //Clear Plugin Info + Result := true; +// clear plugin info ClearPluginInfo(No); - {//Workaround Plugins Loaded 2 Times - For I := low(PluginPaths) to high(PluginPaths) do - if (PluginPaths[I] = Filename) then - exit; } +{ +// workaround plugins loaded 2 times + for i := low(pluginpaths) to high(pluginpaths) do + if (pluginpaths[i] = filename) then + exit; +} - //Load Libary - hLibg := LoadLibrary(PChar(DLLPath +PathDelim+ Filename)); - //If Loaded +// load libary + hLibg := LoadLibrary(PChar(PluginPath.Append(Filename).ToNative)); +// if loaded if (hLibg <> 0) then begin - //Load Info Procedure - @Info := GetProcAddress (hLibg, PChar('PluginInfo')); +// load info procedure + @Info := GetProcAddress(hLibg, PChar('PluginInfo')); - //If Loaded +// if loaded if (@Info <> nil) then begin - //Load PluginInfo - Info (Plugins[No]); - Result := True; +// load plugininfo + Info(Plugins[No]); + Result := true; end else - Log.LogError('Could not Load Plugin "' + Filename + '": Info Procedure not Found'); + Log.LogError('Could not load plugin "' + Filename.ToNative + '": Info procedure not found'); FreeLibrary (hLibg); end - else - Log.LogError('Could not Load Plugin "' + Filename + '": Libary not Loaded'); + else + Log.LogError('Could not load plugin "' + Filename.ToNative + '": Libary not loaded'); end; -function TDLLMan.LoadPlugin(No: Cardinal): boolean; +function TDLLMan.LoadPlugin(No: cardinal): boolean; begin - Result := False; - //Load Libary - hLib := LoadLibrary(PChar(DLLPath +PathDelim+ PluginPaths[No])); - //If Loaded + Result := true; +// load libary + hLib := LoadLibrary(PChar(PluginPath.Append(PluginPaths[No]).ToNative)); +// if loaded if (hLib <> 0) then begin - //Load Info Procedure - @P_Init := GetProcAddress (hLib, PChar('Init')); - @P_Draw := GetProcAddress (hLib, PChar('Draw')); - @P_Finish := GetProcAddress (hLib, PChar('Finish')); +// load info procedure + @P_Init := GetProcAddress (hLib, 'Init'); + @P_Draw := GetProcAddress (hLib, 'Draw'); + @P_Finish := GetProcAddress (hLib, 'Finish'); - //If Loaded - if (@P_Init <> nil) And (@P_Draw <> nil) And (@P_Finish <> nil) then +// if loaded + if (@P_Init <> nil) and (@P_Draw <> nil) and (@P_Finish <> nil) then begin Selected := @Plugins[No]; - Result := True; + Result := true; end else begin - Log.LogError('Could not Load Plugin "' + PluginPaths[No] + '": Procedures not Found'); - + Log.LogError('Could not load plugin "' + PluginPaths[No].ToNative + '": Procedures not found'); end; end - else - Log.LogError('Could not Load Plugin "' + PluginPaths[No] + '": Libary not Loaded'); + else + Log.LogError('Could not load plugin "' + PluginPaths[No].ToNative + '": Libary not loaded'); end; procedure TDLLMan.UnLoadPlugin; begin -if (hLib <> 0) then - FreeLibrary (hLib); - -//Selected := nil; -@P_Init := nil; -@P_Draw := nil; -@P_Finish := nil; -@P_RData := nil; + if (hLib <> 0) then + FreeLibrary (hLib); + +// Selected := nil; + @P_Init := nil; + @P_Draw := nil; + @P_Finish := nil; + @P_RData := nil; end; -function TDLLMan.PluginInit (const TeamInfo: TTeamInfo; var Playerinfo: TPlayerinfo; const Sentences: TSentences; const LoadTex: fModi_LoadTex; const Print: fModi_Print; LoadSound: fModi_LoadSound; PlaySound: pModi_PlaySound): boolean; +function TDLLMan.PluginInit (const TeamInfo: TTeamInfo; + var Playerinfo: TPlayerinfo; + const Sentences: TSentences; + const LoadTex: fModi_LoadTex; + const Print: fModi_Print; + LoadSound: fModi_LoadSound; + PlaySound: pModi_PlaySound) + : boolean; var Methods: TMethodRec; begin @@ -250,26 +265,26 @@ begin if (@P_Init <> nil) then Result := P_Init (TeamInfo, PlayerInfo, Sentences, Methods) else - Result := False + Result := true end; -function TDLLMan.PluginDraw (var Playerinfo: TPlayerinfo; const CurSentence: Cardinal): boolean; +function TDLLMan.PluginDraw (var Playerinfo: TPlayerinfo; const CurSentence: cardinal): boolean; begin -if (@P_Draw <> nil) then - Result := P_Draw (PlayerInfo, CurSentence) -else - Result := False + if (@P_Draw <> nil) then + Result := P_Draw (PlayerInfo, CurSentence) + else + Result := true end; function TDLLMan.PluginFinish (var Playerinfo: TPlayerinfo): byte; begin -if (@P_Finish <> nil) then - Result := P_Finish (PlayerInfo) -else - Result := 0; + if (@P_Finish <> nil) then + Result := P_Finish (PlayerInfo) + else + Result := 0; end; -procedure TDLLMan.PluginRData (handle: HSTREAM; buffer: Pointer; len: DWORD; user: DWORD); +procedure TDLLMan.PluginRData (handle: HStream; buffer: Pointer; len: dword; user: dword); begin if (@P_RData <> nil) then P_RData (handle, buffer, len, user); diff --git a/Lua/src/base/UDataBase.pas b/Lua/src/base/UDataBase.pas index 0f9d88a7..85b4b8e8 100644 --- a/Lua/src/base/UDataBase.pas +++ b/Lua/src/base/UDataBase.pas @@ -34,20 +34,21 @@ interface {$I switches.inc} uses - USongs, - USong, Classes, - SQLiteTable3; + SQLiteTable3, + UPath, + USong, + USongs; //-------------------- -//DataBaseSystem - Class including all DB Methods +//DataBaseSystem - Class including all DB methods //-------------------- type TStatType = ( - stBestScores, // Best Scores - stBestSingers, // Best Singers - stMostSungSong, // Most sung Songs - stMostPopBand // Most popular Band + stBestScores, // Best scores + stBestSingers, // Best singers + stMostSungSong, // Most sung songs + stMostPopBand // Most popular band ); // abstract super-class for statistic results @@ -58,54 +59,56 @@ type TStatResultBestScores = class(TStatResult) public - Singer: WideString; - Score: Word; - Difficulty: Byte; - SongArtist: WideString; - SongTitle: WideString; + Singer: UTF8String; + Score: word; + Difficulty: byte; + SongArtist: UTF8String; + SongTitle: UTF8String; + Date: UTF8String; end; TStatResultBestSingers = class(TStatResult) public - Player: WideString; - AverageScore: Word; + Player: UTF8String; + AverageScore: word; end; TStatResultMostSungSong = class(TStatResult) public - Artist: WideString; - Title: WideString; - TimesSung: Word; + Artist: UTF8String; + Title: UTF8String; + TimesSung: word; end; TStatResultMostPopBand = class(TStatResult) public - ArtistName: WideString; - TimesSungTot: Word; + ArtistName: UTF8String; + TimesSungTot: word; end; - + TDataBaseSystem = class private - ScoreDB: TSQLiteDatabase; - fFilename: string; + ScoreDB: TSQLiteDatabase; + fFilename: IPath; function GetVersion(): integer; procedure SetVersion(Version: integer); public - property Filename: string read fFilename; + property Filename: IPath read fFilename; destructor Destroy; override; - procedure Init(const Filename: string); + procedure Init(const Filename: IPath); procedure ReadScore(Song: TSong); - procedure AddScore(Song: TSong; Level: integer; const Name: WideString; Score: integer); + procedure AddScore(Song: TSong; Level: integer; const Name: UTF8String; Score: integer); procedure WriteScore(Song: TSong); - function GetStats(Typ: TStatType; Count: Byte; Page: Cardinal; Reversed: Boolean): TList; + function GetStats(Typ: TStatType; Count: byte; Page: cardinal; Reversed: boolean): TList; procedure FreeStats(StatList: TList); - function GetTotalEntrys(Typ: TStatType): Cardinal; + function GetTotalEntrys(Typ: TStatType): cardinal; function GetStatReset: TDateTime; + function FormatDate(time_stamp: integer): UTF8String; end; var @@ -114,53 +117,72 @@ var implementation uses - ULog, DateUtils, + ULanguage, StrUtils, - SysUtils; - + SysUtils, + ULog; + +{ + cDBVersion - history + 0 = USDX 1.01 or no Database + 01 = USDX 1.1 +} const cDBVersion = 01; // 0.1 cUS_Scores = 'us_scores'; cUS_Songs = 'us_songs'; - cUS_Statistics_Info = 'us_statistics_info'; + cUS_Statistics_Info = 'us_statistics_info'; (** - * Opens Database and Create Tables if not Exist + * Open database and create tables if they do not exist *) -procedure TDataBaseSystem.Init(const Filename: string); +procedure TDataBaseSystem.Init(const Filename: IPath); var - Version: integer; + Version: integer; + finalizeConversion: boolean; begin if Assigned(ScoreDB) then Exit; - Log.LogStatus('Initializing database: "'+Filename+'"', 'TDataBaseSystem.Init'); + Log.LogStatus('Initializing database: "' + Filename.ToNative + '"', 'TDataBaseSystem.Init'); try - - // Open Database - ScoreDB := TSQLiteDatabase.Create(Filename); + + // open database + ScoreDB := TSQLiteDatabase.Create(Filename.ToUTF8); fFilename := Filename; - // Close and delete outdated file Version := GetVersion(); - if ((Version <> 0) and (Version <> cDBVersion)) then + + // add Table cUS_Statistics_Info + // needed in the conversion from 1.01 to 1.1 + if not ScoreDB.TableExists(cUS_Statistics_Info) then begin - Log.LogInfo('Outdated cover-database file found', 'TDataBaseSystem.Init'); - // Close and delete outdated file - ScoreDB.Free; - if (not DeleteFile(Filename)) then - raise Exception.Create('Could not delete ' + Filename); - // Reopen - ScoreDB := TSQLiteDatabase.Create(Filename); - Version := 0; + Log.LogInfo('Outdated song database found - missing table"' + cUS_Statistics_Info + '"', 'TDataBaseSystem.Init'); + ScoreDB.ExecSQL('CREATE TABLE IF NOT EXISTS [' + cUS_Statistics_Info + '] (' + + '[ResetTime] INTEGER' + + ');'); + // insert creation timestamp + ScoreDB.ExecSQL(Format('INSERT INTO [' + cUS_Statistics_Info + '] ' + + '([ResetTime]) VALUES(%d);', + [DateTimeToUnix(Now())])); end; - + + // convert data from 1.01 to 1.1 + // part #1 - prearrangement + finalizeConversion := false; + if (Version = 0) AND ScoreDB.TableExists('US_Scores') then + begin + // rename old tables - to be able to insert new table structures + ScoreDB.ExecSQL('ALTER TABLE US_Scores RENAME TO us_scores_101;'); + ScoreDB.ExecSQL('ALTER TABLE US_Songs RENAME TO us_songs_101;'); + finalizeConversion := true; // means: conversion has to be done! + end; + // Set version number after creation if (Version = 0) then SetVersion(cDBVersion); - // SQLite does not handle VARCHAR(n) or INT(n) as expected. // Texts do not have a restricted length, no matter which type is used, @@ -169,30 +191,49 @@ begin // types are used (especially FieldAsInteger). Also take care to write the // types in upper-case letters although SQLite does not care about this - // SQLiteTable3 is very sensitive in this regard. - - ScoreDB.ExecSQL('CREATE TABLE IF NOT EXISTS ['+cUS_Scores+'] (' + + ScoreDB.ExecSQL('CREATE TABLE IF NOT EXISTS [' + cUS_Scores + '] (' + '[SongID] INTEGER NOT NULL, ' + '[Difficulty] INTEGER NOT NULL, ' + '[Player] TEXT NOT NULL, ' + - '[Score] INTEGER NOT NULL' + + '[Score] INTEGER NOT NULL, ' + + '[Date] INTEGER NULL' + ');'); - ScoreDB.ExecSQL('CREATE TABLE IF NOT EXISTS ['+cUS_Songs+'] (' + + ScoreDB.ExecSQL('CREATE TABLE IF NOT EXISTS [' + cUS_Songs + '] (' + '[ID] INTEGER PRIMARY KEY, ' + '[Artist] TEXT NOT NULL, ' + '[Title] TEXT NOT NULL, ' + - '[TimesPlayed] INTEGER NOT NULL' + + '[TimesPlayed] INTEGER NOT NULL, ' + + '[Rating] INTEGER NULL' + ');'); - if not ScoreDB.TableExists(cUS_Statistics_Info) then + // convert data from 1.01 to 1.1 + // part #2 - accomplishment + if finalizeConversion then begin - ScoreDB.ExecSQL('CREATE TABLE IF NOT EXISTS ['+cUS_Statistics_Info+'] (' + - '[ResetTime] INTEGER' + - ');'); - // insert creation timestamp - ScoreDB.ExecSQL(Format('INSERT INTO ['+cUS_Statistics_Info+'] ' + - '([ResetTime]) VALUES(%d);', - [DateTimeToUnix(Now())])); + Log.LogInfo('Outdated song database found - begin conversion from V1.01 to V1.1', 'TDataBaseSystem.Init'); + // insert old values into new db-schemes (/tables) + ScoreDB.ExecSQL('INSERT INTO ' + cUS_Scores + ' SELECT SongID, Difficulty, Player, Score FROM us_scores_101;'); + ScoreDB.ExecSQL('INSERT INTO ' + cUS_Songs + ' SELECT ID, Artist, Title, TimesPlayed, ''NULL'' FROM us_songs_101;'); + //now drop old tables + ScoreDB.ExecSQL('DROP TABLE us_scores_101;'); + ScoreDB.ExecSQL('DROP TABLE us_songs_101;'); + end; + + // add column rating to cUS_Songs + // just for users of nightly builds and developers! + if not ScoreDB.ContainsColumn(cUS_Songs, 'Rating') then + begin + Log.LogInfo('Outdated song database found - adding column rating to "' + cUS_Songs + '"', 'TDataBaseSystem.Init'); + ScoreDB.ExecSQL('ALTER TABLE ' + cUS_Songs + ' ADD COLUMN [Rating] INTEGER NULL'); + end; + + + //add column date to cUS-Scores + if not ScoreDB.ContainsColumn(cUS_Scores, 'Date') then + begin + Log.LogInfo('adding column date to "' + cUS_Scores + '"', 'TDataBaseSystem.Init'); + ScoreDB.ExecSQL('ALTER TABLE ' + cUS_Scores + ' ADD COLUMN [Date] INTEGER NULL'); end; except @@ -216,33 +257,55 @@ begin end; (** + * Format a UNIX-Timestamp into DATE (If 0 then '') + *) +function TDataBaseSystem.FormatDate(time_stamp: integer): UTF8String; +var + Year, Month, Day: word; +begin + Result:=''; + try + if time_stamp<>0 then + begin + DecodeDate(UnixToDateTime(time_stamp), Year, Month, Day); + Result := Format(Language.Translate('STAT_FORMAT_DATE'), [Day, Month, Year]); + end; + except + on E: EConvertError do + Log.LogError('Error Parsing FormatString "STAT_FORMAT_DATE": ' + E.Message); + end; +end; + + +(** * Read Scores into SongArray *) procedure TDataBaseSystem.ReadScore(Song: TSong); var - TableData: TSQLiteUniTable; - Difficulty: Integer; + TableData: TSQLiteUniTable; + Difficulty: integer; + I: integer; + PlayerListed: boolean; begin if not Assigned(ScoreDB) then Exit; TableData := nil; - try // Search Song in DB TableData := ScoreDB.GetUniTable( - 'SELECT [Difficulty], [Player], [Score] FROM ['+cUS_Scores+'] ' + + 'SELECT [Difficulty], [Player], [Score], [Date] FROM [' + cUS_Scores + '] ' + 'WHERE [SongID] = (' + - 'SELECT [ID] FROM ['+cUS_Songs+'] ' + + 'SELECT [ID] FROM [' + cUS_Songs + '] ' + 'WHERE [Artist] = ? AND [Title] = ? ' + 'LIMIT 1) ' + - 'ORDER BY [Score] DESC LIMIT 15', - [UTF8Encode(Song.Artist), UTF8Encode(Song.Title)]); + 'ORDER BY [Score] DESC;', //no LIMIT! see filter below! + [Song.Artist, Song.Title]); // Empty Old Scores - SetLength(Song.Score[0], 0); - SetLength(Song.Score[1], 0); - SetLength(Song.Score[2], 0); + SetLength(Song.Score[0], 0); //easy + SetLength(Song.Score[1], 0); //medium + SetLength(Song.Score[2], 0); //hard // Go through all Entrys while (not TableData.EOF) do @@ -252,12 +315,31 @@ begin if ((Difficulty >= 0) and (Difficulty <= 2)) and (Length(Song.Score[Difficulty]) < 5) then begin - SetLength(Song.Score[Difficulty], Length(Song.Score[Difficulty]) + 1); + //filter player + PlayerListed:=false; + if (Length(Song.Score[Difficulty])>0) then + begin + for I := 0 to Length(Song.Score[Difficulty]) - 1 do + begin + if (Song.Score[Difficulty, I].Name = TableData.FieldByName['Player']) then + begin + PlayerListed:=true; + break; + end; + end; + end; - Song.Score[Difficulty, High(Song.Score[Difficulty])].Name := - UTF8Decode(TableData.FieldByName['Player']); - Song.Score[Difficulty, High(Song.Score[Difficulty])].Score := + if not PlayerListed then + begin + SetLength(Song.Score[Difficulty], Length(Song.Score[Difficulty]) + 1); + + Song.Score[Difficulty, High(Song.Score[Difficulty])].Name := + TableData.FieldByName['Player']; + Song.Score[Difficulty, High(Song.Score[Difficulty])].Score := TableData.FieldAsInteger(TableData.FieldIndex['Score']); + Song.Score[Difficulty, High(Song.Score[Difficulty])].Date := + FormatDate(TableData.FieldAsInteger(TableData.FieldIndex['Date'])); + end; end; TableData.Next; @@ -277,70 +359,43 @@ end; (** * Adds one new score to DB *) -procedure TDataBaseSystem.AddScore(Song: TSong; Level: integer; const Name: WideString; Score: integer); +procedure TDataBaseSystem.AddScore(Song: TSong; Level: integer; const Name: UTF8String; Score: integer); var - ID: Integer; + ID: integer; TableData: TSQLiteTable; begin if not Assigned(ScoreDB) then Exit; - // Prevent 0 Scores from being added - if (Score <= 0) then - Exit; + // Prevent 0 Scores from being added EDIT: ==> UScreenTop5.pas! + //if (Score <= 0) then + // Exit; TableData := nil; try ID := ScoreDB.GetTableValue( - 'SELECT [ID] FROM ['+cUS_Songs+'] ' + + 'SELECT [ID] FROM [' + cUS_Songs + '] ' + 'WHERE [Artist] = ? AND [Title] = ?', - [UTF8Encode(Song.Artist), UTF8Encode(Song.Title)]); + [Song.Artist, Song.Title]); if (ID = 0) then begin // Create song if it does not exist ScoreDB.ExecSQL( - 'INSERT INTO ['+cUS_Songs+'] ' + + 'INSERT INTO [' + cUS_Songs + '] ' + '([ID], [Artist], [Title], [TimesPlayed]) VALUES ' + '(NULL, ?, ?, 0);', - [UTF8Encode(Song.Artist), UTF8Encode(Song.Title)]); + [Song.Artist, Song.Title]); // Get song-ID ID := ScoreDB.GetLastInsertRowID(); end; // Create new entry ScoreDB.ExecSQL( - 'INSERT INTO ['+cUS_Scores+'] ' + - '([SongID] ,[Difficulty], [Player], [Score]) VALUES ' + - '(?, ?, ?, ?);', - [ID, Level, UTF8Encode(Name), Score]); - - // Delete last position when there are more than 5 entrys. - // Fixes crash when there are > 5 ScoreEntrys - // Note: GetUniTable is not applicable here, as the results are used while - // table entries are deleted. - TableData := ScoreDB.GetTable( - 'SELECT [Player], [Score] FROM ['+cUS_Scores+'] ' + - 'WHERE [SongID] = ' + InttoStr(ID) + ' AND ' + - '[Difficulty] = ' + InttoStr(Level) +' ' + - 'ORDER BY [Score] DESC LIMIT -1 OFFSET 5'); - - while (not TableData.EOF) do - begin - // Note: Score is an int-value, so in contrast to Player, we do not bind - // this value. Otherwise we had to convert the string to an int to avoid - // an automatic cast of this field to the TEXT type (although it might even - // work that way). - ScoreDB.ExecSQL( - 'DELETE FROM ['+cUS_Scores+'] ' + - 'WHERE [SongID] = ' + InttoStr(ID) + ' AND ' + - '[Difficulty] = ' + InttoStr(Level) +' AND ' + - '[Player] = ? AND ' + - '[Score] = ' + TableData.FieldByName['Score'], - [TableData.FieldByName['Player']]); - - TableData.Next; - end; + 'INSERT INTO [' + cUS_Scores + '] ' + + '([SongID] ,[Difficulty], [Player], [Score], [Date]) VALUES ' + + '(?, ?, ?, ?, ?);', + [ID, Level, Name, Score, DateTimeToUnix(Now())]); except on E: Exception do Log.LogError(E.Message, 'TDataBaseSystem.AddScore'); @@ -350,21 +405,21 @@ begin end; (** - * Not needed with new System. - * Used for increment played count + * Not needed with new system. + * Used to increment played count *) procedure TDataBaseSystem.WriteScore(Song: TSong); begin if not Assigned(ScoreDB) then Exit; - + try // Increase TimesPlayed ScoreDB.ExecSQL( - 'UPDATE ['+cUS_Songs+'] ' + + 'UPDATE [' + cUS_Songs + '] ' + 'SET [TimesPlayed] = [TimesPlayed] + 1 ' + 'WHERE [Title] = ? AND [Artist] = ?;', - [UTF8Encode(Song.Title), UTF8Encode(Song.Artist)]); + [Song.Title, Song.Artist]); except on E: Exception do Log.LogError(E.Message, 'TDataBaseSystem.WriteScore'); end; @@ -376,11 +431,11 @@ end; * entries. * Free the result-list with FreeStats() after usage to avoid memory leaks. *) -function TDataBaseSystem.GetStats(Typ: TStatType; Count: Byte; Page: Cardinal; Reversed: Boolean): TList; +function TDataBaseSystem.GetStats(Typ: TStatType; Count: byte; Page: cardinal; Reversed: boolean): TList; var - Query: String; + Query: string; TableData: TSQLiteUniTable; - Stat: TStatResult; + Stat: TStatResult; begin Result := nil; @@ -392,19 +447,19 @@ begin // Create query case Typ of stBestScores: begin - Query := 'SELECT [Player], [Difficulty], [Score], [Artist], [Title] FROM ['+cUS_Scores+'] ' + - 'INNER JOIN ['+cUS_Songs+'] ON ([SongID] = [ID]) ORDER BY [Score]'; + Query := 'SELECT [Player], [Difficulty], [Score], [Artist], [Title], [Date] FROM [' + cUS_Scores + '] ' + + 'INNER JOIN [' + cUS_Songs + '] ON ([SongID] = [ID]) ORDER BY [Score]'; end; stBestSingers: begin - Query := 'SELECT [Player], ROUND(AVG([Score])) FROM ['+cUS_Scores+'] ' + + Query := 'SELECT [Player], ROUND(AVG([Score])) FROM [' + cUS_Scores + '] ' + 'GROUP BY [Player] ORDER BY AVG([Score])'; end; stMostSungSong: begin - Query := 'SELECT [Artist], [Title], [TimesPlayed] FROM ['+cUS_Songs+'] ' + + Query := 'SELECT [Artist], [Title], [TimesPlayed] FROM [' + cUS_Songs + '] ' + 'ORDER BY [TimesPlayed]'; end; stMostPopBand: begin - Query := 'SELECT [Artist], SUM([TimesPlayed]) FROM ['+cUS_Songs+'] ' + + Query := 'SELECT [Artist], SUM([TimesPlayed]) FROM [' + cUS_Songs + '] ' + 'GROUP BY [Artist] ORDER BY SUM([TimesPlayed])'; end; end; @@ -437,18 +492,19 @@ begin Stat := TStatResultBestScores.Create; with TStatResultBestScores(Stat) do begin - Singer := UTF8Decode(TableData.Fields[0]); + Singer := TableData.Fields[0]; Difficulty := TableData.FieldAsInteger(1); Score := TableData.FieldAsInteger(2); - SongArtist := UTF8Decode(TableData.Fields[3]); - SongTitle := UTF8Decode(TableData.Fields[4]); + SongArtist := TableData.Fields[3]; + SongTitle := TableData.Fields[4]; + Date := FormatDate(TableData.FieldAsInteger(5)); end; end; stBestSingers: begin Stat := TStatResultBestSingers.Create; with TStatResultBestSingers(Stat) do begin - Player := UTF8Decode(TableData.Fields[0]); + Player := TableData.Fields[0]; AverageScore := TableData.FieldAsInteger(1); end; end; @@ -456,8 +512,8 @@ begin Stat := TStatResultMostSungSong.Create; with TStatResultMostSungSong(Stat) do begin - Artist := UTF8Decode(TableData.Fields[0]); - Title := UTF8Decode(TableData.Fields[1]); + Artist := TableData.Fields[0]; + Title := TableData.Fields[1]; TimesSung := TableData.FieldAsInteger(2); end; end; @@ -465,7 +521,7 @@ begin Stat := TStatResultMostPopBand.Create; with TStatResultMostPopBand(Stat) do begin - ArtistName := UTF8Decode(TableData.Fields[0]); + ArtistName := TableData.Fields[0]; TimesSungTot := TableData.FieldAsInteger(1); end; end @@ -484,21 +540,21 @@ end; procedure TDataBaseSystem.FreeStats(StatList: TList); var - I: integer; + Index: integer; begin if (StatList = nil) then Exit; - for I := 0 to StatList.Count-1 do - TStatResult(StatList[I]).Free; + for Index := 0 to StatList.Count-1 do + TStatResult(StatList[Index]).Free; StatList.Free; end; (** * Gets total number of entrys for a stats query *) -function TDataBaseSystem.GetTotalEntrys(Typ: TStatType): Cardinal; +function TDataBaseSystem.GetTotalEntrys(Typ: TStatType): cardinal; var - Query: String; + Query: string; begin Result := 0; @@ -509,13 +565,13 @@ begin // Create query case Typ of stBestScores: - Query := 'SELECT COUNT([SongID]) FROM ['+cUS_Scores+'];'; + Query := 'SELECT COUNT([SongID]) FROM [' + cUS_Scores + '];'; stBestSingers: - Query := 'SELECT COUNT(DISTINCT [Player]) FROM ['+cUS_Scores+'];'; + Query := 'SELECT COUNT(DISTINCT [Player]) FROM [' + cUS_Scores + '];'; stMostSungSong: - Query := 'SELECT COUNT([ID]) FROM ['+cUS_Songs+'];'; + Query := 'SELECT COUNT([ID]) FROM [' + cUS_Songs + '];'; stMostPopBand: - Query := 'SELECT COUNT(DISTINCT [Artist]) FROM ['+cUS_Songs+'];'; + Query := 'SELECT COUNT(DISTINCT [Artist]) FROM [' + cUS_Songs + '];'; end; Result := ScoreDB.GetTableValue(Query); @@ -538,7 +594,7 @@ begin Exit; try - Query := 'SELECT [ResetTime] FROM ['+cUS_Statistics_Info+'];'; + Query := 'SELECT [ResetTime] FROM [' + cUS_Statistics_Info + '];'; Result := UnixToDateTime(ScoreDB.GetTableValue(Query)); except on E: Exception do Log.LogError(E.Message, 'TDataBaseSystem.GetStatReset'); diff --git a/Lua/src/base/UDraw.pas b/Lua/src/base/UDraw.pas index f1bdcad0..47863f62 100644 --- a/Lua/src/base/UDraw.pas +++ b/Lua/src/base/UDraw.pas @@ -55,7 +55,6 @@ procedure SingDrawTimeBar(); //Draw Editor NoteLines procedure EditDrawLine(Left, Top, Right: real; NrLines: integer; Space: integer); - type TRecR = record Top: real; @@ -67,7 +66,6 @@ type WMid: real; Height: real; HMid: real; - Mid: real; end; @@ -75,48 +73,45 @@ var NotesW: real; NotesH: real; Starfr: integer; - StarfrG: integer; + StarfrG: integer; //SingBar - TickOld: cardinal; - TickOld2:cardinal; + TickOld: cardinal; + TickOld2: cardinal; implementation uses + SysUtils, + Math, gl, + TextGL, + UDLLManager, + UDrawTexture, UGraphic, - SysUtils, + UIni, + ULog, + ULyrics, + UNote, UMusic, URecord, - ULog, UScreenSing, UScreenSingModi, - ULyrics, - UMain, - TextGL, - UTexture, - UDrawTexture, - UIni, - Math, - UDLLManager; + UTexture; procedure SingDrawBackground; var - Rec: TRecR; - TexRec: TRecR; + Rec: TRecR; + TexRec: TRecR; begin - if (ScreenSing.Tex_Background.TexNum > 0) then begin - - glClearColor (1, 1, 1, 1); - glColor4f (1, 1, 1, 1); - + if (ScreenSing.Tex_Background.TexNum > 0) then + begin if (Ini.MovieSize <= 1) then //HalfSize BG begin (* half screen + gradient *) Rec.Top := 110; // 80 Rec.Bottom := Rec.Top + 20; - Rec.Left := 0; + Rec.Left := 0; Rec.Right := 800; TexRec.Top := (Rec.Top / 600) * ScreenSing.Tex_Background.TexH; @@ -182,16 +177,17 @@ end; procedure SingDrawOscilloscope(X, Y, W, H: real; NrSound: integer); var SampleIndex: integer; - Sound: TCaptureBuffer; - MaxX, MaxY: real; + Sound: TCaptureBuffer; + MaxX, MaxY: real; begin; Sound := AudioInputProcessor.Sound[NrSound]; // Log.LogStatus('Oscilloscope', 'SingDraw'); glColor3f(Skin_OscR, Skin_OscG, Skin_OscB); - {if (ParamStr(1) = '-black') or (ParamStr(1) = '-fsblack') then - glColor3f(1, 1, 1); } - +{ + if (ParamStr(1) = '-black') or (ParamStr(1) = '-fsblack') then + glColor3f(1, 1, 1); +} MaxX := W-1; MaxY := (H-1) / 2; @@ -208,16 +204,15 @@ begin; Sound.UnlockAnalysisBuffer(); end; - - procedure SingDrawNoteLines(Left, Top, Right: real; Space: integer); var - Count: integer; + Count: integer; begin glEnable(GL_BLEND); glColor4f(Skin_P1_LinesR, Skin_P1_LinesG, Skin_P1_LinesB, 0.4); glBegin(GL_LINES); - for Count := 0 to 9 do begin + for Count := 0 to 9 do + begin glVertex2f(Left, Top + Count * Space); glVertex2f(Right, Top + Count * Space); end; @@ -227,13 +222,14 @@ end; procedure SingDrawBeatDelimeters(Left, Top, Right: real; NrLines: integer); var - Count: integer; - TempR: real; + Count: integer; + TempR: real; begin TempR := (Right-Left) / (Lines[NrLines].Line[Lines[NrLines].Current].End_ - Lines[NrLines].Line[Lines[NrLines].Current].Note[0].Start); glEnable(GL_BLEND); glBegin(GL_LINES); - for Count := Lines[NrLines].Line[Lines[NrLines].Current].Note[0].Start to Lines[NrLines].Line[Lines[NrLines].Current].End_ do begin + for Count := Lines[NrLines].Line[Lines[NrLines].Current].Note[0].Start to Lines[NrLines].Line[Lines[NrLines].Current].End_ do + begin if (Count mod Lines[NrLines].Resolution) = Lines[NrLines].NotesGAP then glColor4f(0, 0, 0, 1) else @@ -248,18 +244,17 @@ end; // draw blank Notebars procedure SingDrawLine(Left, Top, Right: real; NrLines: integer; Space: integer); var - Rec: TRecR; - Count: integer; - TempR: real; + Rec: TRecR; + Count: integer; + TempR: real; + + PlayerNumber: integer; - PlayerNumber: Integer; + GoldenStarPos: real; - GoldenStarPos : real; - - lTmpA , - lTmpB : real; + lTmpA, lTmpB : real; begin -// We actually don't have a playernumber in this procedure, it should reside in NrLines - but it's always set to zero +// We actually don't have a playernumber in this procedure, it should reside in NrLines - but it is always set to zero // So we exploit this behavior a bit - we give NrLines the playernumber, keep it in playernumber - and then we set NrLines to zero // This could also come quite in handy when we do the duet mode, cause just the notes for the player that has to sing should be drawn then // BUT this is not implemented yet, all notes are drawn! :D @@ -279,22 +274,19 @@ begin lTmpA := (Right-Left); lTmpB := (Lines[NrLines].Line[Lines[NrLines].Current].End_ - Lines[NrLines].Line[Lines[NrLines].Current].Note[0].Start); - if ( lTmpA > 0 ) AND - ( lTmpB > 0 ) THEN - begin - TempR := lTmpA / lTmpB; - end - else - begin - TempR := 0; - end; - - - with Lines[NrLines].Line[Lines[NrLines].Current] do begin - for Count := 0 to HighNote do begin - with Note[Count] do begin - if NoteType <> ntFreestyle then begin + if ( lTmpA > 0 ) and ( lTmpB > 0 ) then + TempR := lTmpA / lTmpB + else + TempR := 0; + with Lines[NrLines].Line[Lines[NrLines].Current] do + begin + for Count := 0 to HighNote do + begin + with Note[Count] do + begin + if NoteType <> ntFreestyle then + begin if Ini.EffectSing = 0 then // If Golden note Effect of then Change not Color @@ -307,18 +299,18 @@ begin else glColor4f(1, 1, 1, 1); // We set alpha to 1, cause we can control the transparency through the png itself - // left part - Rec.Left := (Start-Lines[NrLines].Line[Lines[NrLines].Current].Note[0].Start) * TempR + Left + 0.5 + 10*ScreenX; - Rec.Right := Rec.Left + NotesW; - Rec.Top := Top - (Tone-BaseNote)*Space/2 - NotesH; - Rec.Bottom := Rec.Top + 2 * NotesH; - glBindTexture(GL_TEXTURE_2D, Tex_plain_Left[PlayerNumber].TexNum); - glBegin(GL_QUADS); - glTexCoord2f(0, 0); glVertex2f(Rec.Left, Rec.Top); - glTexCoord2f(0, 1); glVertex2f(Rec.Left, Rec.Bottom); - glTexCoord2f(1, 1); glVertex2f(Rec.Right, Rec.Bottom); - glTexCoord2f(1, 0); glVertex2f(Rec.Right, Rec.Top); - glEnd; + // left part + Rec.Left := (Start-Lines[NrLines].Line[Lines[NrLines].Current].Note[0].Start) * TempR + Left + 0.5 + 10*ScreenX; + Rec.Right := Rec.Left + NotesW; + Rec.Top := Top - (Tone-BaseNote)*Space/2 - NotesH; + Rec.Bottom := Rec.Top + 2 * NotesH; + glBindTexture(GL_TEXTURE_2D, Tex_plain_Left[PlayerNumber].TexNum); + glBegin(GL_QUADS); + glTexCoord2f(0, 0); glVertex2f(Rec.Left, Rec.Top); + glTexCoord2f(0, 1); glVertex2f(Rec.Left, Rec.Bottom); + glTexCoord2f(1, 1); glVertex2f(Rec.Right, Rec.Bottom); + glTexCoord2f(1, 0); glVertex2f(Rec.Right, Rec.Top); + glEnd; //We keep the postion of the top left corner b4 it's overwritten GoldenStarPos := Rec.Left; @@ -338,9 +330,9 @@ begin glTexCoord2f(round((Rec.Right-Rec.Left)/32), 0); glVertex2f(Rec.Right, Rec.Top); glEnd; - // right part - Rec.Left := Rec.Right; - Rec.Right := Rec.Right + NotesW; + // right part + Rec.Left := Rec.Right; + Rec.Right := Rec.Right + NotesW; glBindTexture(GL_TEXTURE_2D, Tex_plain_Right[PlayerNumber].TexNum); glBegin(GL_QUADS); @@ -350,11 +342,11 @@ begin glTexCoord2f(1, 0); glVertex2f(Rec.Right, Rec.Top); glEnd; - // Golden Star Patch - if (NoteType = ntGolden) AND (Ini.EffectSing=1) then - begin - GoldenRec.SaveGoldenStarsRec(GoldenStarPos, Rec.Top, Rec.Right, Rec.Bottom); - end; + // Golden Star Patch + if (NoteType = ntGolden) and (Ini.EffectSing=1) then + begin + GoldenRec.SaveGoldenStarsRec(GoldenStarPos, Rec.Top, Rec.Right, Rec.Bottom); + end; end; // if not FreeStyle end; // with @@ -366,127 +358,132 @@ begin end; end; - // draw sung notes procedure SingDrawPlayerLine(X, Y, W: real; PlayerIndex: integer; Space: integer); var TempR: real; Rec: TRecR; N: integer; - //R, G, B, A: real; +// R, G, B, A: real; NotesH2: real; begin - if (ScreenSing.settings.NotesVisible and (1 shl PlayerIndex) <> 0) then - begin - glColor3f(1, 1, 1); - glEnable(GL_TEXTURE_2D); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + //Log.LogStatus('Player notes', 'SingDraw'); +{ + if NrGracza = 0 then + LoadColor(R, G, B, 'P1Light') + else + LoadColor(R, G, B, 'P2Light'); +} + //R := 71/255; + //G := 175/255; + //B := 247/255; - //if Player[NrGracza].LengthNote > 0 then + glColor3f(1, 1, 1); + glEnable(GL_TEXTURE_2D); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + //if Player[NrGracza].LengthNote > 0 then + begin + TempR := W / (Lines[0].Line[Lines[0].Current].End_ - Lines[0].Line[Lines[0].Current].Note[0].Start); + for N := 0 to Player[PlayerIndex].HighNote do begin - TempR := W / (Lines[0].Line[Lines[0].Current].End_ - Lines[0].Line[Lines[0].Current].Note[0].Start); - for N := 0 to Player[PlayerIndex].HighNote do + with Player[PlayerIndex].Note[N] do begin - with Player[PlayerIndex].Note[N] do - begin - // Left part of note - Rec.Left := X + (Start-Lines[0].Line[Lines[0].Current].Note[0].Start) * TempR + 0.5 + 10*ScreenX; - Rec.Right := Rec.Left + NotesW; + // Left part of note + Rec.Left := X + (Start-Lines[0].Line[Lines[0].Current].Note[0].Start) * TempR + 0.5 + 10*ScreenX; + Rec.Right := Rec.Left + NotesW; - // Draw it in half size, if not hit - if Hit then - begin - NotesH2 := NotesH - end - else - begin - NotesH2 := int(NotesH * 0.65); - end; + // Draw it in half size, if not hit + if Hit then + begin + NotesH2 := NotesH + end + else + begin + NotesH2 := int(NotesH * 0.65); + end; - Rec.Top := Y - (Tone-Lines[0].Line[Lines[0].Current].BaseNote)*Space/2 - NotesH2; - Rec.Bottom := Rec.Top + 2 *NotesH2; + Rec.Top := Y - (Tone-Lines[0].Line[Lines[0].Current].BaseNote)*Space/2 - NotesH2; + Rec.Bottom := Rec.Top + 2 * NotesH2; - // draw the left part - glColor3f(1, 1, 1); - glBindTexture(GL_TEXTURE_2D, Tex_Left[PlayerIndex+1].TexNum); - glBegin(GL_QUADS); - glTexCoord2f(0, 0); glVertex2f(Rec.Left, Rec.Top); - glTexCoord2f(0, 1); glVertex2f(Rec.Left, Rec.Bottom); - glTexCoord2f(1, 1); glVertex2f(Rec.Right, Rec.Bottom); - glTexCoord2f(1, 0); glVertex2f(Rec.Right, Rec.Top); - glEnd; + // draw the left part + glColor3f(1, 1, 1); + glBindTexture(GL_TEXTURE_2D, Tex_Left[PlayerIndex+1].TexNum); + glBegin(GL_QUADS); + glTexCoord2f(0, 0); glVertex2f(Rec.Left, Rec.Top); + glTexCoord2f(0, 1); glVertex2f(Rec.Left, Rec.Bottom); + glTexCoord2f(1, 1); glVertex2f(Rec.Right, Rec.Bottom); + glTexCoord2f(1, 0); glVertex2f(Rec.Right, Rec.Top); + glEnd; - // Middle part of the note - Rec.Left := Rec.Right; - Rec.Right := X + (Start+Length-Lines[0].Line[Lines[0].Current].Note[0].Start) * TempR - NotesW - 0.5 + 10*ScreenX; - - // new - if (Start+Length-1 = LyricsState.CurrentBeatD) then - Rec.Right := Rec.Right - (1-Frac(LyricsState.MidBeatD)) * TempR; - // the left note is more right than the right note itself, sounds weird - so we fix that xD - if Rec.Right <= Rec.Left then - Rec.Right := Rec.Left; - - // draw the middle part - glBindTexture(GL_TEXTURE_2D, Tex_Mid[PlayerIndex+1].TexNum); - glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT ); - glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT ); - glBegin(GL_QUADS); - glTexCoord2f(0, 0); glVertex2f(Rec.Left, Rec.Top); - glTexCoord2f(0, 1); glVertex2f(Rec.Left, Rec.Bottom); - glTexCoord2f(round((Rec.Right-Rec.Left)/32), 1); glVertex2f(Rec.Right, Rec.Bottom); - glTexCoord2f(round((Rec.Right-Rec.Left)/32), 0); glVertex2f(Rec.Right, Rec.Top); - glEnd; - glColor3f(1, 1, 1); + // Middle part of the note + Rec.Left := Rec.Right; + Rec.Right := X + (Start+Length-Lines[0].Line[Lines[0].Current].Note[0].Start) * TempR - NotesW - 0.5 + 10*ScreenX; + + // new + if (Start+Length-1 = LyricsState.CurrentBeatD) then + Rec.Right := Rec.Right - (1-Frac(LyricsState.MidBeatD)) * TempR; + // the left note is more right than the right note itself, sounds weird - so we fix that xD + if Rec.Right <= Rec.Left then + Rec.Right := Rec.Left; + + // draw the middle part + glBindTexture(GL_TEXTURE_2D, Tex_Mid[PlayerIndex+1].TexNum); + glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT ); + glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT ); + glBegin(GL_QUADS); + glTexCoord2f(0, 0); glVertex2f(Rec.Left, Rec.Top); + glTexCoord2f(0, 1); glVertex2f(Rec.Left, Rec.Bottom); + glTexCoord2f(round((Rec.Right-Rec.Left)/32), 1); glVertex2f(Rec.Right, Rec.Bottom); + glTexCoord2f(round((Rec.Right-Rec.Left)/32), 0); glVertex2f(Rec.Right, Rec.Top); + glEnd; + glColor3f(1, 1, 1); - // the right part of the note - Rec.Left := Rec.Right; - Rec.Right := Rec.Right + NotesW; + // the right part of the note + Rec.Left := Rec.Right; + Rec.Right := Rec.Right + NotesW; - glBindTexture(GL_TEXTURE_2D, Tex_Right[PlayerIndex+1].TexNum); - glBegin(GL_QUADS); - glTexCoord2f(0, 0); glVertex2f(Rec.Left, Rec.Top); - glTexCoord2f(0, 1); glVertex2f(Rec.Left, Rec.Bottom); - glTexCoord2f(1, 1); glVertex2f(Rec.Right, Rec.Bottom); - glTexCoord2f(1, 0); glVertex2f(Rec.Right, Rec.Top); - glEnd; + glBindTexture(GL_TEXTURE_2D, Tex_Right[PlayerIndex+1].TexNum); + glBegin(GL_QUADS); + glTexCoord2f(0, 0); glVertex2f(Rec.Left, Rec.Top); + glTexCoord2f(0, 1); glVertex2f(Rec.Left, Rec.Bottom); + glTexCoord2f(1, 1); glVertex2f(Rec.Right, Rec.Bottom); + glTexCoord2f(1, 0); glVertex2f(Rec.Right, Rec.Top); + glEnd; - // Perfect note is stored - if Perfect and (Ini.EffectSing=1) then + // Perfect note is stored + if Perfect and (Ini.EffectSing=1) then + begin + //A := 1 - 2*(LyricsState.GetCurrentTime() - GetTimeFromBeat(Start+Length)); + if not (Start+Length-1 = LyricsState.CurrentBeatD) then begin - //A := 1 - 2*(LyricsState.GetCurrentTime() - GetTimeFromBeat(Start+Length)); - if not (Start+Length-1 = LyricsState.CurrentBeatD) then - begin - //Star animation counter - //inc(Starfr); - //Starfr := Starfr mod 128; - GoldenRec.SavePerfectNotePos(Rec.Left, Rec.Top); - end; + //Star animation counter + //inc(Starfr); + //Starfr := Starfr mod 128; + GoldenRec.SavePerfectNotePos(Rec.Left, Rec.Top); end; - end; // with - end; // for + end; + end; // with + end; // for - // actually we need a comparison here, to determine if the singing process - // is ahead Rec.Right even if there is no singing + // actually we need a comparison here, to determine if the singing process + // is ahead Rec.Right even if there is no singing - if (Ini.EffectSing = 1) then - GoldenRec.GoldenNoteTwinkle(Rec.Top,Rec.Bottom,Rec.Right, PlayerIndex); - end; // if - end; + if (Ini.EffectSing = 1) then + GoldenRec.GoldenNoteTwinkle(Rec.Top,Rec.Bottom,Rec.Right, PlayerIndex); + end; // if end; //draw Note glow procedure SingDrawPlayerBGLine(Left, Top, Right: real; NrLines, PlayerIndex: integer; Space: integer); var - Rec: TRecR; - Count: integer; - TempR: real; + Rec: TRecR; + Count: integer; + TempR: real; X1, X2, X3, X4: real; - W, H: real; - - lTmpA , - lTmpB : real; + W, H: real; + lTmpA, lTmpB: real; begin if (ScreenSing.settings.NotesVisible and (1 shl PlayerIndex) <> 0) then begin @@ -498,15 +495,10 @@ begin lTmpA := (Right-Left); lTmpB := (Lines[NrLines].Line[Lines[NrLines].Current].End_ - Lines[NrLines].Line[Lines[NrLines].Current].Note[0].Start); - if ( lTmpA > 0 ) and - ( lTmpB > 0 ) then - begin - TempR := lTmpA / lTmpB; - end + if ( lTmpA > 0 ) and ( lTmpB > 0 ) then + TempR := lTmpA / lTmpB else - begin TempR := 0; - end; with Lines[NrLines].Line[Lines[NrLines].Current] do begin @@ -528,7 +520,7 @@ begin X4 := X3+W; // left - Rec.Left := X1; + Rec.Left := X1; Rec.Right := X2; Rec.Top := Top - (Tone-BaseNote)*Space/2 - H; Rec.Bottom := Rec.Top + 2 * H; @@ -542,7 +534,7 @@ begin glEnd; // middle part - Rec.Left := X2; + Rec.Left := X2; Rec.Right := X3; glBindTexture(GL_TEXTURE_2D, Tex_BG_Mid[PlayerIndex+1].TexNum); @@ -554,7 +546,7 @@ begin glEnd; // right part - Rec.Left := X3; + Rec.Left := X3; Rec.Right := X4; glBindTexture(GL_TEXTURE_2D, Tex_BG_Right[PlayerIndex+1].TexNum); @@ -602,7 +594,7 @@ begin // FIXME: accessing ScreenSing is not that generic LyricEngine := ScreenSing.Lyrics; - + // do not draw the lyrics helper if the current line does not contain any note if (Length(CurLine.Note) > 0) then begin @@ -650,7 +642,7 @@ begin // determine lyric help bar position and size Bounds.Left := MoveStartX + BarProgress * MoveDist; Bounds.Right := Bounds.Left + BarWidth; - Bounds.Top := Skin_LyricsT + 3; + Bounds.Top := Theme.LyricBar.IndicatorYOffset + Theme.LyricBar.UpperY ; Bounds.Bottom := Bounds.Top + BarHeight + 3; // draw lyric help bar @@ -690,9 +682,6 @@ begin // FIXME: accessing ScreenSing is not that generic LyricEngine := ScreenSing.Lyrics; - // background //BG Fullsize Mod - //SingDrawBackground; - // draw time-bar SingDrawTimeBar(); @@ -700,7 +689,7 @@ begin // to-do : needs fix when party mode works w/ 2 screens if (PlayersPlay = 1) and (Ini.NoteLines = 1) and (ScreenSing.settings.NotesVisible and (1) <> 0) then - SingDrawNoteLines(Nr.Left + 10*ScreenX, Skin_P2_NotesB - 105, Nr.Right + 10*ScreenX, 15); + SingDrawNoteLines(NR.Left + 10*ScreenX, Skin_P2_NotesB - 105, NR.Right + 10*ScreenX, 15); if ((PlayersPlay = 2) or (PlayersPlay = 4)) and (Ini.NoteLines = 1) then begin @@ -724,39 +713,48 @@ begin SingDrawLyricHelper(NR.Left, NR.WMid); // oscilloscope - if Ini.Oscilloscope = 1 then begin + if Ini.Oscilloscope = 1 then + begin if PlayersPlay = 1 then SingDrawOscilloscope(190 + 10*ScreenX, 55, 180, 40, 0); - if PlayersPlay = 2 then begin + if PlayersPlay = 2 then + begin SingDrawOscilloscope(190 + 10*ScreenX, 55, 180, 40, 0); SingDrawOscilloscope(425 + 10*ScreenX, 55, 180, 40, 1); end; - if PlayersPlay = 4 then begin - if ScreenAct = 1 then begin + if PlayersPlay = 4 then + begin + if ScreenAct = 1 then + begin SingDrawOscilloscope(190 + 10*ScreenX, 55, 180, 40, 0); SingDrawOscilloscope(425 + 10*ScreenX, 55, 180, 40, 1); end; - if ScreenAct = 2 then begin + if ScreenAct = 2 then + begin SingDrawOscilloscope(190 + 10*ScreenX, 55, 180, 40, 2); SingDrawOscilloscope(425 + 10*ScreenX, 55, 180, 40, 3); end; end; - if PlayersPlay = 3 then begin - SingDrawOscilloscope(75 + 10*ScreenX, 95, 100, 20, 0); + if PlayersPlay = 3 then + begin + SingDrawOscilloscope( 75 + 10*ScreenX, 95, 100, 20, 0); SingDrawOscilloscope(370 + 10*ScreenX, 95, 100, 20, 1); SingDrawOscilloscope(670 + 10*ScreenX, 95, 100, 20, 2); end; - if PlayersPlay = 6 then begin - if ScreenAct = 1 then begin + if PlayersPlay = 6 then + begin + if ScreenAct = 1 then + begin SingDrawOscilloscope( 75 + 10*ScreenX, 95, 100, 20, 0); SingDrawOscilloscope(370 + 10*ScreenX, 95, 100, 20, 1); SingDrawOscilloscope(670 + 10*ScreenX, 95, 100, 20, 2); end; - if ScreenAct = 2 then begin + if ScreenAct = 2 then + begin SingDrawOscilloscope( 75 + 10*ScreenX, 95, 100, 20, 3); SingDrawOscilloscope(370 + 10*ScreenX, 95, 100, 20, 4); SingDrawOscilloscope(670 + 10*ScreenX, 95, 100, 20, 5); @@ -785,104 +783,121 @@ begin end; // Draw the Notes - if PlayersPlay = 1 then begin + if PlayersPlay = 1 then + begin SingDrawPlayerBGLine(NR.Left + 20, Skin_P2_NotesB, NR.Right - 20, 0, 0, 15); // Background glow - colorized in playercolor SingDrawLine(NR.Left + 20, Skin_P2_NotesB, NR.Right - 20, 0, 15); // Plain unsung notes - colorized in playercolor - SingDrawPlayerLine(Nr.Left + 20, Skin_P2_NotesB, Nr.Width - 40, 0, 15); // imho the sung notes + SingDrawPlayerLine(NR.Left + 20, Skin_P2_NotesB, NR.Width - 40, 0, 15); // imho the sung notes end; - if (PlayersPlay = 2) then begin - SingDrawPlayerBGLine(Nr.Left + 20, Skin_P1_NotesB, Nr.Right - 20, 0, 0, 15); - SingDrawPlayerBGLine(Nr.Left + 20, Skin_P2_NotesB, Nr.Right - 20, 0, 1, 15); + if PlayersPlay = 2 then + begin + SingDrawPlayerBGLine(NR.Left + 20, Skin_P1_NotesB, NR.Right - 20, 0, 0, 15); + SingDrawPlayerBGLine(NR.Left + 20, Skin_P2_NotesB, NR.Right - 20, 0, 1, 15); SingDrawLine(NR.Left + 20, Skin_P1_NotesB, NR.Right - 20, 0, 15); SingDrawLine(NR.Left + 20, Skin_P2_NotesB, NR.Right - 20, 1, 15); - SingDrawPlayerLine(Nr.Left + 20, Skin_P1_NotesB, Nr.Width - 40, 0, 15); - SingDrawPlayerLine(Nr.Left + 20, Skin_P2_NotesB, Nr.Width - 40, 1, 15); + SingDrawPlayerLine(NR.Left + 20, Skin_P1_NotesB, NR.Width - 40, 0, 15); + SingDrawPlayerLine(NR.Left + 20, Skin_P2_NotesB, NR.Width - 40, 1, 15); end; - if PlayersPlay = 3 then begin + if PlayersPlay = 3 then + begin NotesW := NotesW * 0.8; NotesH := NotesH * 0.8; - SingDrawPlayerBGLine(Nr.Left + 20, 120+95, Nr.Right - 20, 0, 0, 12); - SingDrawPlayerBGLine(Nr.Left + 20, 245+95, Nr.Right - 20, 0, 1, 12); - SingDrawPlayerBGLine(Nr.Left + 20, 370+95, Nr.Right - 20, 0, 2, 12); + SingDrawPlayerBGLine(NR.Left + 20, 120+95, NR.Right - 20, 0, 0, 12); + SingDrawPlayerBGLine(NR.Left + 20, 245+95, NR.Right - 20, 0, 1, 12); + SingDrawPlayerBGLine(NR.Left + 20, 370+95, NR.Right - 20, 0, 2, 12); SingDrawLine(NR.Left + 20, 120+95, NR.Right - 20, 0, 12); SingDrawLine(NR.Left + 20, 245+95, NR.Right - 20, 1, 12); SingDrawLine(NR.Left + 20, 370+95, NR.Right - 20, 2, 12); - SingDrawPlayerLine(Nr.Left + 20, 120+95, Nr.Width - 40, 0, 12); - SingDrawPlayerLine(Nr.Left + 20, 245+95, Nr.Width - 40, 1, 12); - SingDrawPlayerLine(Nr.Left + 20, 370+95, Nr.Width - 40, 2, 12); + SingDrawPlayerLine(NR.Left + 20, 120+95, NR.Width - 40, 0, 12); + SingDrawPlayerLine(NR.Left + 20, 245+95, NR.Width - 40, 1, 12); + SingDrawPlayerLine(NR.Left + 20, 370+95, NR.Width - 40, 2, 12); end; - if PlayersPlay = 4 then begin - if ScreenAct = 1 then begin - SingDrawPlayerBGLine(Nr.Left + 20, Skin_P1_NotesB, Nr.Right - 20, 0, 0, 15); - SingDrawPlayerBGLine(Nr.Left + 20, Skin_P2_NotesB, Nr.Right - 20, 0, 1, 15); + if PlayersPlay = 4 then + begin + if ScreenAct = 1 then + begin + SingDrawPlayerBGLine(NR.Left + 20, Skin_P1_NotesB, NR.Right - 20, 0, 0, 15); + SingDrawPlayerBGLine(NR.Left + 20, Skin_P2_NotesB, NR.Right - 20, 0, 1, 15); end; - if ScreenAct = 2 then begin - SingDrawPlayerBGLine(Nr.Left + 20, Skin_P1_NotesB, Nr.Right - 20, 0, 2, 15); - SingDrawPlayerBGLine(Nr.Left + 20, Skin_P2_NotesB, Nr.Right - 20, 0, 3, 15); + if ScreenAct = 2 then + begin + SingDrawPlayerBGLine(NR.Left + 20, Skin_P1_NotesB, NR.Right - 20, 0, 2, 15); + SingDrawPlayerBGLine(NR.Left + 20, Skin_P2_NotesB, NR.Right - 20, 0, 3, 15); end; - if ScreenAct = 1 then begin + if ScreenAct = 1 then + begin SingDrawLine(NR.Left + 20, Skin_P1_NotesB, NR.Right - 20, 0, 15); SingDrawLine(NR.Left + 20, Skin_P2_NotesB, NR.Right - 20, 1, 15); end; - if ScreenAct = 2 then begin + if ScreenAct = 2 then + begin SingDrawLine(NR.Left + 20, Skin_P1_NotesB, NR.Right - 20, 2, 15); SingDrawLine(NR.Left + 20, Skin_P2_NotesB, NR.Right - 20, 3, 15); end; - if ScreenAct = 1 then begin - SingDrawPlayerLine(Nr.Left + 20, Skin_P1_NotesB, Nr.Width - 40, 0, 15); - SingDrawPlayerLine(Nr.Left + 20, Skin_P2_NotesB, Nr.Width - 40, 1, 15); + if ScreenAct = 1 then + begin + SingDrawPlayerLine(NR.Left + 20, Skin_P1_NotesB, NR.Width - 40, 0, 15); + SingDrawPlayerLine(NR.Left + 20, Skin_P2_NotesB, NR.Width - 40, 1, 15); end; - if ScreenAct = 2 then begin - SingDrawPlayerLine(Nr.Left + 20, Skin_P1_NotesB, Nr.Width - 40, 2, 15); - SingDrawPlayerLine(Nr.Left + 20, Skin_P2_NotesB, Nr.Width - 40, 3, 15); + if ScreenAct = 2 then + begin + SingDrawPlayerLine(NR.Left + 20, Skin_P1_NotesB, NR.Width - 40, 2, 15); + SingDrawPlayerLine(NR.Left + 20, Skin_P2_NotesB, NR.Width - 40, 3, 15); end; end; - if PlayersPlay = 6 then begin + if PlayersPlay = 6 then + begin NotesW := NotesW * 0.8; NotesH := NotesH * 0.8; - if ScreenAct = 1 then begin - SingDrawPlayerBGLine(Nr.Left + 20, 120+95, Nr.Right - 20, 0, 0, 12); - SingDrawPlayerBGLine(Nr.Left + 20, 245+95, Nr.Right - 20, 0, 1, 12); - SingDrawPlayerBGLine(Nr.Left + 20, 370+95, Nr.Right - 20, 0, 2, 12); + if ScreenAct = 1 then + begin + SingDrawPlayerBGLine(NR.Left + 20, 120+95, NR.Right - 20, 0, 0, 12); + SingDrawPlayerBGLine(NR.Left + 20, 245+95, NR.Right - 20, 0, 1, 12); + SingDrawPlayerBGLine(NR.Left + 20, 370+95, NR.Right - 20, 0, 2, 12); end; - if ScreenAct = 2 then begin - SingDrawPlayerBGLine(Nr.Left + 20, 120+95, Nr.Right - 20, 0, 3, 12); - SingDrawPlayerBGLine(Nr.Left + 20, 245+95, Nr.Right - 20, 0, 4, 12); - SingDrawPlayerBGLine(Nr.Left + 20, 370+95, Nr.Right - 20, 0, 5, 12); + if ScreenAct = 2 then + begin + SingDrawPlayerBGLine(NR.Left + 20, 120+95, NR.Right - 20, 0, 3, 12); + SingDrawPlayerBGLine(NR.Left + 20, 245+95, NR.Right - 20, 0, 4, 12); + SingDrawPlayerBGLine(NR.Left + 20, 370+95, NR.Right - 20, 0, 5, 12); end; - if ScreenAct = 1 then begin + if ScreenAct = 1 then + begin SingDrawLine(NR.Left + 20, 120+95, NR.Right - 20, 0, 12); SingDrawLine(NR.Left + 20, 245+95, NR.Right - 20, 1, 12); SingDrawLine(NR.Left + 20, 370+95, NR.Right - 20, 2, 12); end; - if ScreenAct = 2 then begin + if ScreenAct = 2 then + begin SingDrawLine(NR.Left + 20, 120+95, NR.Right - 20, 3, 12); SingDrawLine(NR.Left + 20, 245+95, NR.Right - 20, 4, 12); SingDrawLine(NR.Left + 20, 370+95, NR.Right - 20, 5, 12); end; - if ScreenAct = 1 then begin - SingDrawPlayerLine(Nr.Left + 20, 120+95, Nr.Width - 40, 0, 12); - SingDrawPlayerLine(Nr.Left + 20, 245+95, Nr.Width - 40, 1, 12); - SingDrawPlayerLine(Nr.Left + 20, 370+95, Nr.Width - 40, 2, 12); + if ScreenAct = 1 then + begin + SingDrawPlayerLine(NR.Left + 20, 120+95, NR.Width - 40, 0, 12); + SingDrawPlayerLine(NR.Left + 20, 245+95, NR.Width - 40, 1, 12); + SingDrawPlayerLine(NR.Left + 20, 370+95, NR.Width - 40, 2, 12); end; - if ScreenAct = 2 then begin - SingDrawPlayerLine(Nr.Left + 20, 120+95, Nr.Width - 40, 3, 12); - SingDrawPlayerLine(Nr.Left + 20, 245+95, Nr.Width - 40, 4, 12); - SingDrawPlayerLine(Nr.Left + 20, 370+95, Nr.Width - 40, 5, 12); + if ScreenAct = 2 then + begin + SingDrawPlayerLine(NR.Left + 20, 120+95, NR.Width - 40, 3, 12); + SingDrawPlayerLine(NR.Left + 20, 245+95, NR.Width - 40, 4, 12); + SingDrawPlayerLine(NR.Left + 20, 370+95, NR.Width - 40, 5, 12); end; end; glDisable(GL_BLEND); @@ -892,12 +907,15 @@ end; // q'n'd for using the game mode dll's procedure SingModiDraw (PlayerInfo: TPlayerInfo); var - NR: TRecR; + NR: TRecR; begin // positions - if Ini.SingWindow = 0 then begin + if Ini.SingWindow = 0 then + begin NR.Left := 120; - end else begin + end + else + begin NR.Left := 20; end; @@ -912,16 +930,18 @@ begin if DLLMan.Selected.ShowNotes then begin if PlayersPlay = 1 then - SingDrawNoteLines(Nr.Left + 10*ScreenX, Skin_P2_NotesB - 105, Nr.Right + 10*ScreenX, 15); - if (PlayersPlay = 2) or (PlayersPlay = 4) then begin - SingDrawNoteLines(Nr.Left + 10*ScreenX, Skin_P1_NotesB - 105, Nr.Right + 10*ScreenX, 15); - SingDrawNoteLines(Nr.Left + 10*ScreenX, Skin_P2_NotesB - 105, Nr.Right + 10*ScreenX, 15); + SingDrawNoteLines(NR.Left + 10*ScreenX, Skin_P2_NotesB - 105, NR.Right + 10*ScreenX, 15); + if (PlayersPlay = 2) or (PlayersPlay = 4) then + begin + SingDrawNoteLines(NR.Left + 10*ScreenX, Skin_P1_NotesB - 105, NR.Right + 10*ScreenX, 15); + SingDrawNoteLines(NR.Left + 10*ScreenX, Skin_P2_NotesB - 105, NR.Right + 10*ScreenX, 15); end; - if (PlayersPlay = 3) or (PlayersPlay = 6) then begin - SingDrawNoteLines(Nr.Left + 10*ScreenX, 120, Nr.Right + 10*ScreenX, 12); - SingDrawNoteLines(Nr.Left + 10*ScreenX, 245, Nr.Right + 10*ScreenX, 12); - SingDrawNoteLines(Nr.Left + 10*ScreenX, 370, Nr.Right + 10*ScreenX, 12); + if (PlayersPlay = 3) or (PlayersPlay = 6) then + begin + SingDrawNoteLines(NR.Left + 10*ScreenX, 120, NR.Right + 10*ScreenX, 12); + SingDrawNoteLines(NR.Left + 10*ScreenX, 245, NR.Right + 10*ScreenX, 12); + SingDrawNoteLines(NR.Left + 10*ScreenX, 370, NR.Right + 10*ScreenX, 12); end; end; @@ -930,26 +950,31 @@ begin // TODO: Lyrics helper // oscilloscope | the thing that moves when you yell into your mic (imho) - if (((Ini.Oscilloscope = 1) AND (DLLMan.Selected.ShowRateBar_O)) AND (NOT DLLMan.Selected.ShowRateBar)) then begin + if (((Ini.Oscilloscope = 1) and (DLLMan.Selected.ShowRateBar_O)) and (not DLLMan.Selected.ShowRateBar)) then + begin if PlayersPlay = 1 then if PlayerInfo.Playerinfo[0].Enabled then SingDrawOscilloscope(190 + 10*ScreenX, 55, 180, 40, 0); - if PlayersPlay = 2 then begin + if PlayersPlay = 2 then + begin if PlayerInfo.Playerinfo[0].Enabled then SingDrawOscilloscope(190 + 10*ScreenX, 55, 180, 40, 0); if PlayerInfo.Playerinfo[1].Enabled then SingDrawOscilloscope(425 + 10*ScreenX, 55, 180, 40, 1); end; - if PlayersPlay = 4 then begin - if ScreenAct = 1 then begin + if PlayersPlay = 4 then + begin + if ScreenAct = 1 then + begin if PlayerInfo.Playerinfo[0].Enabled then SingDrawOscilloscope(190 + 10*ScreenX, 55, 180, 40, 0); if PlayerInfo.Playerinfo[1].Enabled then SingDrawOscilloscope(425 + 10*ScreenX, 55, 180, 40, 1); end; - if ScreenAct = 2 then begin + if ScreenAct = 2 then + begin if PlayerInfo.Playerinfo[2].Enabled then SingDrawOscilloscope(190 + 10*ScreenX, 55, 180, 40, 2); if PlayerInfo.Playerinfo[3].Enabled then @@ -957,17 +982,20 @@ begin end; end; - if PlayersPlay = 3 then begin + if PlayersPlay = 3 then + begin if PlayerInfo.Playerinfo[0].Enabled then - SingDrawOscilloscope(75 + 10*ScreenX, 95, 100, 20, 0); + SingDrawOscilloscope( 75 + 10*ScreenX, 95, 100, 20, 0); if PlayerInfo.Playerinfo[1].Enabled then SingDrawOscilloscope(370 + 10*ScreenX, 95, 100, 20, 1); if PlayerInfo.Playerinfo[2].Enabled then SingDrawOscilloscope(670 + 10*ScreenX, 95, 100, 20, 2); end; - if PlayersPlay = 6 then begin - if ScreenAct = 1 then begin + if PlayersPlay = 6 then + begin + if ScreenAct = 1 then + begin if PlayerInfo.Playerinfo[0].Enabled then SingDrawOscilloscope( 75 + 10*ScreenX, 95, 100, 20, 0); if PlayerInfo.Playerinfo[1].Enabled then @@ -975,7 +1003,8 @@ begin if PlayerInfo.Playerinfo[2].Enabled then SingDrawOscilloscope(670 + 10*ScreenX, 95, 100, 20, 2); end; - if ScreenAct = 2 then begin + if ScreenAct = 2 then + begin if PlayerInfo.Playerinfo[3].Enabled then SingDrawOscilloscope( 75 + 10*ScreenX, 95, 100, 20, 3); if PlayerInfo.Playerinfo[4].Enabled then @@ -1006,107 +1035,120 @@ begin end; end; - if (DLLMAn.Selected.ShowNotes And DLLMan.Selected.LoadSong) then + if (DLLMAn.Selected.ShowNotes and DLLMan.Selected.LoadSong) then begin - if (PlayersPlay = 1) And PlayerInfo.Playerinfo[0].Enabled then begin + if (PlayersPlay = 1) and PlayerInfo.Playerinfo[0].Enabled then + begin SingDrawPlayerBGLine(NR.Left + 20, Skin_P2_NotesB, NR.Right - 20, 0, 0, 15); SingDrawLine(NR.Left + 20, Skin_P2_NotesB, NR.Right - 20, 0, 15); - SingDrawPlayerLine(Nr.Left + 20, Skin_P2_NotesB, Nr.Width - 40, 0, 15); + SingDrawPlayerLine(NR.Left + 20, Skin_P2_NotesB, NR.Width - 40, 0, 15); end; - if (PlayersPlay = 2) then begin + if PlayersPlay = 2 then + begin if PlayerInfo.Playerinfo[0].Enabled then begin - SingDrawPlayerBGLine(Nr.Left + 20, Skin_P1_NotesB, Nr.Right - 20, 0, 0, 15); + SingDrawPlayerBGLine(NR.Left + 20, Skin_P1_NotesB, NR.Right - 20, 0, 0, 15); SingDrawLine(NR.Left + 20, Skin_P1_NotesB, NR.Right - 20, 0, 15); - SingDrawPlayerLine(Nr.Left + 20, Skin_P1_NotesB, Nr.Width - 40, 0, 15); + SingDrawPlayerLine(NR.Left + 20, Skin_P1_NotesB, NR.Width - 40, 0, 15); end; if PlayerInfo.Playerinfo[1].Enabled then begin - SingDrawPlayerBGLine(Nr.Left + 20, Skin_P2_NotesB, Nr.Right - 20, 0, 1, 15); + SingDrawPlayerBGLine(NR.Left + 20, Skin_P2_NotesB, NR.Right - 20, 0, 1, 15); SingDrawLine(NR.Left + 20, Skin_P2_NotesB, NR.Right - 20, 0, 15); - SingDrawPlayerLine(Nr.Left + 20, Skin_P2_NotesB, Nr.Width - 40, 1, 15); + SingDrawPlayerLine(NR.Left + 20, Skin_P2_NotesB, NR.Width - 40, 1, 15); end; end; - if PlayersPlay = 3 then begin + if PlayersPlay = 3 then + begin NotesW := NotesW * 0.8; NotesH := NotesH * 0.8; if PlayerInfo.Playerinfo[0].Enabled then begin - SingDrawPlayerBGLine(Nr.Left + 20, 120+95, Nr.Right - 20, 0, 0, 12); + SingDrawPlayerBGLine(NR.Left + 20, 120+95, NR.Right - 20, 0, 0, 12); SingDrawLine(NR.Left + 20, 120+95, NR.Right - 20, 0, 12); - SingDrawPlayerLine(Nr.Left + 20, 120+95, Nr.Width - 40, 0, 12); + SingDrawPlayerLine(NR.Left + 20, 120+95, NR.Width - 40, 0, 12); end; if PlayerInfo.Playerinfo[1].Enabled then begin - SingDrawPlayerBGLine(Nr.Left + 20, 245+95, Nr.Right - 20, 0, 1, 12); + SingDrawPlayerBGLine(NR.Left + 20, 245+95, NR.Right - 20, 0, 1, 12); SingDrawLine(NR.Left + 20, 245+95, NR.Right - 20, 0, 12); - SingDrawPlayerLine(Nr.Left + 20, 245+95, Nr.Width - 40, 1, 12); + SingDrawPlayerLine(NR.Left + 20, 245+95, NR.Width - 40, 1, 12); end; if PlayerInfo.Playerinfo[2].Enabled then begin - SingDrawPlayerBGLine(Nr.Left + 20, 370+95, Nr.Right - 20, 0, 2, 12); + SingDrawPlayerBGLine(NR.Left + 20, 370+95, NR.Right - 20, 0, 2, 12); SingDrawLine(NR.Left + 20, 370+95, NR.Right - 20, 0, 12); - SingDrawPlayerLine(Nr.Left + 20, 370+95, Nr.Width - 40, 2, 12); + SingDrawPlayerLine(NR.Left + 20, 370+95, NR.Width - 40, 2, 12); end; end; - if PlayersPlay = 4 then begin - if ScreenAct = 1 then begin - SingDrawPlayerBGLine(Nr.Left + 20, Skin_P1_NotesB, Nr.Right - 20, 0, 0, 15); - SingDrawPlayerBGLine(Nr.Left + 20, Skin_P2_NotesB, Nr.Right - 20, 0, 1, 15); + if PlayersPlay = 4 then + begin + if ScreenAct = 1 then + begin + SingDrawPlayerBGLine(NR.Left + 20, Skin_P1_NotesB, NR.Right - 20, 0, 0, 15); + SingDrawPlayerBGLine(NR.Left + 20, Skin_P2_NotesB, NR.Right - 20, 0, 1, 15); end; - if ScreenAct = 2 then begin - SingDrawPlayerBGLine(Nr.Left + 20, Skin_P1_NotesB, Nr.Right - 20, 0, 2, 15); - SingDrawPlayerBGLine(Nr.Left + 20, Skin_P2_NotesB, Nr.Right - 20, 0, 3, 15); + if ScreenAct = 2 then + begin + SingDrawPlayerBGLine(NR.Left + 20, Skin_P1_NotesB, NR.Right - 20, 0, 2, 15); + SingDrawPlayerBGLine(NR.Left + 20, Skin_P2_NotesB, NR.Right - 20, 0, 3, 15); end; SingDrawLine(NR.Left + 20, Skin_P1_NotesB, NR.Right - 20, 0, 15); SingDrawLine(NR.Left + 20, Skin_P2_NotesB, NR.Right - 20, 0, 15); - if ScreenAct = 1 then begin - SingDrawPlayerLine(Nr.Left + 20, Skin_P1_NotesB, Nr.Width - 40, 0, 15); - SingDrawPlayerLine(Nr.Left + 20, Skin_P2_NotesB, Nr.Width - 40, 1, 15); + if ScreenAct = 1 then + begin + SingDrawPlayerLine(NR.Left + 20, Skin_P1_NotesB, NR.Width - 40, 0, 15); + SingDrawPlayerLine(NR.Left + 20, Skin_P2_NotesB, NR.Width - 40, 1, 15); end; - if ScreenAct = 2 then begin - SingDrawPlayerLine(Nr.Left + 20, Skin_P1_NotesB, Nr.Width - 40, 2, 15); - SingDrawPlayerLine(Nr.Left + 20, Skin_P2_NotesB, Nr.Width - 40, 3, 15); + if ScreenAct = 2 then + begin + SingDrawPlayerLine(NR.Left + 20, Skin_P1_NotesB, NR.Width - 40, 2, 15); + SingDrawPlayerLine(NR.Left + 20, Skin_P2_NotesB, NR.Width - 40, 3, 15); end; end; - if PlayersPlay = 6 then begin + if PlayersPlay = 6 then + begin NotesW := NotesW * 0.8; NotesH := NotesH * 0.8; - if ScreenAct = 1 then begin - SingDrawPlayerBGLine(Nr.Left + 20, 120+95, Nr.Right - 20, 0, 0, 12); - SingDrawPlayerBGLine(Nr.Left + 20, 245+95, Nr.Right - 20, 0, 1, 12); - SingDrawPlayerBGLine(Nr.Left + 20, 370+95, Nr.Right - 20, 0, 2, 12); + if ScreenAct = 1 then + begin + SingDrawPlayerBGLine(NR.Left + 20, 120+95, NR.Right - 20, 0, 0, 12); + SingDrawPlayerBGLine(NR.Left + 20, 245+95, NR.Right - 20, 0, 1, 12); + SingDrawPlayerBGLine(NR.Left + 20, 370+95, NR.Right - 20, 0, 2, 12); end; - if ScreenAct = 2 then begin - SingDrawPlayerBGLine(Nr.Left + 20, 120+95, Nr.Right - 20, 0, 3, 12); - SingDrawPlayerBGLine(Nr.Left + 20, 245+95, Nr.Right - 20, 0, 4, 12); - SingDrawPlayerBGLine(Nr.Left + 20, 370+95, Nr.Right - 20, 0, 5, 12); + if ScreenAct = 2 then + begin + SingDrawPlayerBGLine(NR.Left + 20, 120+95, NR.Right - 20, 0, 3, 12); + SingDrawPlayerBGLine(NR.Left + 20, 245+95, NR.Right - 20, 0, 4, 12); + SingDrawPlayerBGLine(NR.Left + 20, 370+95, NR.Right - 20, 0, 5, 12); end; SingDrawLine(NR.Left + 20, 120+95, NR.Right - 20, 0, 12); SingDrawLine(NR.Left + 20, 245+95, NR.Right - 20, 0, 12); SingDrawLine(NR.Left + 20, 370+95, NR.Right - 20, 0, 12); - if ScreenAct = 1 then begin - SingDrawPlayerLine(Nr.Left + 20, 120+95, Nr.Width - 40, 0, 12); - SingDrawPlayerLine(Nr.Left + 20, 245+95, Nr.Width - 40, 1, 12); - SingDrawPlayerLine(Nr.Left + 20, 370+95, Nr.Width - 40, 2, 12); + if ScreenAct = 1 then + begin + SingDrawPlayerLine(NR.Left + 20, 120+95, NR.Width - 40, 0, 12); + SingDrawPlayerLine(NR.Left + 20, 245+95, NR.Width - 40, 1, 12); + SingDrawPlayerLine(NR.Left + 20, 370+95, NR.Width - 40, 2, 12); end; - if ScreenAct = 2 then begin - SingDrawPlayerLine(Nr.Left + 20, 120+95, Nr.Width - 40, 3, 12); - SingDrawPlayerLine(Nr.Left + 20, 245+95, Nr.Width - 40, 4, 12); - SingDrawPlayerLine(Nr.Left + 20, 370+95, Nr.Width - 40, 5, 12); + if ScreenAct = 2 then + begin + SingDrawPlayerLine(NR.Left + 20, 120+95, NR.Width - 40, 3, 12); + SingDrawPlayerLine(NR.Left + 20, 245+95, NR.Width - 40, 4, 12); + SingDrawPlayerLine(NR.Left + 20, 370+95, NR.Width - 40, 5, 12); end; end; end; @@ -1115,15 +1157,14 @@ begin glDisable(GL_TEXTURE_2D); end; - {//SingBar Mod procedure SingDrawSingbar(X, Y, W, H: real; Percent: integer); var - R: Real; - G: Real; - B: Real; - A: cardinal; - I: Integer; + R: real; + G: real; + B: real; + A: cardinal; + I: integer; begin; @@ -1141,7 +1182,7 @@ begin; glEnd; //SingBar coloured Bar - Case Percent of + case Percent of 0..22: begin R := 1; G := 0; @@ -1167,7 +1208,7 @@ begin; G := 1; B := 0; end; - End; //Case + end; //case glColor4f(R, G, B, 1); glEnable(GL_TEXTURE_2D); @@ -1198,99 +1239,103 @@ end; //end Singbar Mod //PhrasenBonus - Line Bonus Pop Up -procedure SingDrawLineBonus( const X, Y: Single; Color: TRGB; Alpha: Single; Text: string; Age: Integer); +procedure SingDrawLineBonus(const X, Y: Single; Color: TRGB; Alpha: Single; Text: string; Age: integer); var -Length, X2: Real; //Length of Text -Size: Integer; //Size of Popup -begin -if Alpha <> 0 then + Length, X2: real; //Length of Text + Size: integer; //Size of Popup begin + if Alpha <> 0 then + begin //Set Font Propertys -SetFontStyle(2); //Font: Outlined1 -if Age < 5 then SetFontSize((Age + 1) * 3) else SetFontSize(18); -SetFontItalic(False); + SetFontStyle(2); //Font: Outlined1 + if Age < 5 then + SetFontSize((Age + 1) * 3) + else + SetFontSize(18); + SetFontItalic(False); //Check Font Size -Length := glTextWidth (Text) + 3; //Little Space for a Better Look ^^ + Length := glTextWidth (Text) + 3; //Little Space for a Better Look ^^ //Text -SetFontPos (X + 50 - (Length / 2), Y + 12); //Position - + SetFontPos (X + 50 - (Length / 2), Y + 12); //Position -if Age < 5 then Size := Age * 10 else Size := 50; - - //Draw Background - //glColor4f(Color.R, Color.G, Color.B, Alpha); //Set Color - glColor4f(1, 1, 1, Alpha); - - - glEnable(GL_TEXTURE_2D); - glEnable(GL_BLEND); - //glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + if Age < 5 then + Size := Age * 10 + else + Size := 50; +//Draw Background +// glColor4f(Color.R, Color.G, Color.B, Alpha); //Set Color + glColor4f(1, 1, 1, Alpha); - //New Method, Not Variable - glBindTexture(GL_TEXTURE_2D, Tex_SingLineBonusBack[2].TexNum); + glEnable(GL_TEXTURE_2D); + glEnable(GL_BLEND); +// glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glBegin(GL_QUADS); - glTexCoord2f(0, 0); glVertex2f(X + 50 - Size, Y + 25 - (Size/2)); - glTexCoord2f(0, 1); glVertex2f(X + 50 - Size, Y + 25 + (Size/2)); - glTexCoord2f(1, 1); glVertex2f(X + 50 + Size, Y + 25 + (Size/2)); - glTexCoord2f(1, 0); glVertex2f(X + 50 + Size, Y + 25 - (Size/2)); - glEnd; +//New Method, Not Variable + glBindTexture(GL_TEXTURE_2D, Tex_SingLineBonusBack[2].TexNum); - glColor4f(1, 1, 1, Alpha); //Set Color - //Draw Text - glPrint (Text); -end; + glBegin(GL_QUADS); + glTexCoord2f(0, 0); glVertex2f(X + 50 - Size, Y + 25 - (Size/2)); + glTexCoord2f(0, 1); glVertex2f(X + 50 - Size, Y + 25 + (Size/2)); + glTexCoord2f(1, 1); glVertex2f(X + 50 + Size, Y + 25 + (Size/2)); + glTexCoord2f(1, 0); glVertex2f(X + 50 + Size, Y + 25 - (Size/2)); + glEnd; + + glColor4f(1, 1, 1, Alpha); //Set Color +//Draw Text + glPrint (Text); + end; end; //PhrasenBonus - Line Bonus Mod} // Draw Note Bars for Editor -//There are 11 Resons for a new Procdedure: (nice binary :D ) -// 1. It don't look good when you Draw the Golden Note Star Effect in the Editor -// 2. You can see the Freestyle Notes in the Editor SemiTransparent -// 3. Its easier and Faster then changing the old Procedure +// There are 11 reasons for a new procedure: (nice binary :D ) +// 1. It does not look good when you draw the golden note star effect in the editor +// 2. You can see the freestyle notes in the editor semitransparent +// 3. It is easier and faster then changing the old procedure procedure EditDrawLine(Left, Top, Right: real; NrLines: integer; Space: integer); var - Rec: TRecR; - Count: integer; - TempR: real; + Rec: TRecR; + Count: integer; + TempR: real; begin glColor3f(1, 1, 1); glEnable(GL_TEXTURE_2D); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); TempR := (Right-Left) / (Lines[NrLines].Line[Lines[NrLines].Current].End_ - Lines[NrLines].Line[Lines[NrLines].Current].Note[0].Start); - with Lines[NrLines].Line[Lines[NrLines].Current] do begin - for Count := 0 to HighNote do begin - with Note[Count] do begin - - // Golden Note Patch - case NoteType of - ntFreestyle: glColor4f(1, 1, 1, 0.35); - ntNormal: glColor4f(1, 1, 1, 0.85); - ntGolden: Glcolor4f(1, 1, 0.3, 0.85); - end; // case - - + with Lines[NrLines].Line[Lines[NrLines].Current] do + begin + for Count := 0 to HighNote do + begin + with Note[Count] do + begin - // left part - Rec.Left := (Start-Lines[NrLines].Line[Lines[NrLines].Current].Note[0].Start) * TempR + Left + 0.5 + 10*ScreenX; - Rec.Right := Rec.Left + NotesW; - Rec.Top := Top - (Tone-BaseNote)*Space/2 - NotesH; - Rec.Bottom := Rec.Top + 2 * NotesH; - glBindTexture(GL_TEXTURE_2D, Tex_Left[Color].TexNum); - glBegin(GL_QUADS); - glTexCoord2f(0, 0); glVertex2f(Rec.Left, Rec.Top); - glTexCoord2f(0, 1); glVertex2f(Rec.Left, Rec.Bottom); - glTexCoord2f(1, 1); glVertex2f(Rec.Right, Rec.Bottom); - glTexCoord2f(1, 0); glVertex2f(Rec.Right, Rec.Top); - glEnd; + // Golden Note Patch + case NoteType of + ntFreestyle: glColor4f(1, 1, 1, 0.35); + ntNormal: glColor4f(1, 1, 1, 0.85); + ntGolden: Glcolor4f(1, 1, 0.3, 0.85); + end; // case + + // left part + Rec.Left := (Start-Lines[NrLines].Line[Lines[NrLines].Current].Note[0].Start) * TempR + Left + 0.5 + 10*ScreenX; + Rec.Right := Rec.Left + NotesW; + Rec.Top := Top - (Tone-BaseNote)*Space/2 - NotesH; + Rec.Bottom := Rec.Top + 2 * NotesH; + glBindTexture(GL_TEXTURE_2D, Tex_Left[Color].TexNum); + glBegin(GL_QUADS); + glTexCoord2f(0, 0); glVertex2f(Rec.Left, Rec.Top); + glTexCoord2f(0, 1); glVertex2f(Rec.Left, Rec.Bottom); + glTexCoord2f(1, 1); glVertex2f(Rec.Right, Rec.Bottom); + glTexCoord2f(1, 0); glVertex2f(Rec.Right, Rec.Top); + glEnd; - // middle part - Rec.Left := Rec.Right; + // middle part + Rec.Left := Rec.Right; Rec.Right := (Start+Length-Lines[NrLines].Line[Lines[NrLines].Current].Note[0].Start) * TempR + Left - NotesW - 0.5 + 10*ScreenX; glBindTexture(GL_TEXTURE_2D, Tex_Mid[Color].TexNum); @@ -1302,7 +1347,7 @@ begin glEnd; // right part - Rec.Left := Rec.Right; + Rec.Left := Rec.Right; Rec.Right := Rec.Right + NotesW; glBindTexture(GL_TEXTURE_2D, Tex_Right[Color].TexNum); @@ -1323,7 +1368,7 @@ end; procedure SingDrawTimeBar(); var - x,y: real; + x, y: real; width, height: real; LyricsProgress: real; CurLyricsTime: real; diff --git a/Lua/src/base/UEditorLyrics.pas b/Lua/src/base/UEditorLyrics.pas index fe8c3ee5..0eacd1f9 100644 --- a/Lua/src/base/UEditorLyrics.pas +++ b/Lua/src/base/UEditorLyrics.pas @@ -40,7 +40,7 @@ uses UTexture; type - alignment = (left, center, right); + TAlignmentType = (atLeft, atCenter, atRight); TWord = record X: real; @@ -58,7 +58,7 @@ type TEditorLyrics = class private - AlignI: alignment; + AlignI: TAlignmentType; XR: real; YR: real; SizeR: real; @@ -69,12 +69,12 @@ type procedure SetX(Value: real); procedure SetY(Value: real); function GetClientX: real; - procedure SetAlign(Value: alignment); + procedure SetAlign(Value: TAlignmentType); function GetSize: real; procedure SetSize(Value: real); procedure SetSelected(Value: integer); procedure SetFontStyle(Value: integer); - procedure AddWord(Text: string); + procedure AddWord(Text: UTF8String); procedure Refresh; public ColR: real; @@ -96,7 +96,7 @@ type property X: real write SetX; property Y: real write SetY; property ClientX: real read GetClientX; - property Align: alignment write SetAlign; + property Align: TAlignmentType write SetAlign; property Size: real read GetSize write SetSize; property Selected: integer read SelectedI write SetSelected; property FontStyle: integer write SetFontStyle; @@ -137,7 +137,7 @@ begin Result := Word[0].X; end; -procedure TEditorLyrics.SetAlign(Value: alignment); +procedure TEditorLyrics.SetAlign(Value: TAlignmentType); begin AlignI := Value; end; @@ -179,7 +179,7 @@ begin FontStyleI := Value; end; -procedure TEditorLyrics.AddWord(Text: string); +procedure TEditorLyrics.AddWord(Text: UTF8String); var WordNum: integer; begin @@ -229,7 +229,7 @@ var WordIndex: integer; TotalWidth: real; begin - if AlignI = center then + if AlignI = atCenter then begin TotalWidth := 0; for WordIndex := 0 to High(Word) do diff --git a/Lua/src/base/UFiles.pas b/Lua/src/base/UFiles.pas index add81f23..5a258e3e 100644 --- a/Lua/src/base/UFiles.pas +++ b/Lua/src/base/UFiles.pas @@ -34,32 +34,33 @@ interface uses SysUtils, + Classes, ULog, UMusic, USongs, - USong; + USong, + UPath; procedure ResetSingTemp; -function SaveSong(Song: TSong; Lines: TLines; Name: string; Relative: boolean): boolean; +type + TSaveSongResult = (ssrOK, ssrFileError, ssrEncodingError); -var - SongFile: TextFile; // all procedures in this unit operates on this file - FileLineNo: integer; //Line which is readed at Last, for error reporting - - // variables available for all procedures - Base : array[0..1] of integer; - Rel : array[0..1] of integer; - Mult : integer = 1; - MultBPM : integer = 4; +{** + * 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, - UMain; + UUnicodeUtils, + UTextEncoding; //-------------------- // Resets the temporary Sentence Arrays for each Player and some other Variables @@ -73,106 +74,139 @@ begin SetLength(Lines[Count].Line, 1); SetLength(Lines[Count].Line[0].Note, 0); Lines[Count].Line[0].Lyric := ''; - Lines[Count].Line[0].LyricWidth := 0; Player[Count].Score := 0; Player[Count].LengthNote := 0; Player[Count].HighNote := -1; end; - - (* FIXME - //Reset Path and Filename Values to Prevent Errors in Editor - if assigned( CurrentSong ) then - begin - SetLength(CurrentSong.BPM, 0); - CurrentSong.Path := ''; - CurrentSong.FileName := ''; - end; - *) - -// CurrentSong := nil; end; - //-------------------- // Saves a Song //-------------------- -function SaveSong(Song: TSong; Lines: TLines; Name: string; Relative: boolean): boolean; +function SaveSong(const Song: TSong; const Lines: TLines; const Name: IPath; Relative: boolean): TSaveSongResult; var C: integer; N: integer; - S: string; + S: AnsiString; B: integer; - RelativeSubTime: integer; - NoteState: String; + RelativeSubTime: integer; + NoteState: AnsiString; + SongFile: TTextFileStream; -begin -// Relative := true; // override (idea - use shift+S to save with relative) - AssignFile(SongFile, Name); - Rewrite(SongFile); - - Writeln(SongFile, '#TITLE:' + Song.Title + ''); - Writeln(SongFile, '#ARTIST:' + Song.Artist); - - if Song.Creator <> '' then Writeln(SongFile, '#CREATOR:' + Song.Creator); - if Song.Edition <> 'Unknown' then Writeln(SongFile, '#EDITION:' + Song.Edition); - if Song.Genre <> 'Unknown' then Writeln(SongFile, '#GENRE:' + Song.Genre); - if Song.Language <> 'Unknown' then Writeln(SongFile, '#LANGUAGE:' + Song.Language); - - Writeln(SongFile, '#MP3:' + Song.Mp3); - - if Song.Cover <> '' then Writeln(SongFile, '#COVER:' + Song.Cover); - if Song.Background <> '' then Writeln(SongFile, '#BACKGROUND:' + Song.Background); - if Song.Video <> '' then Writeln(SongFile, '#VIDEO:' + Song.Video); - if Song.VideoGAP <> 0 then Writeln(SongFile, '#VIDEOGAP:' + FloatToStr(Song.VideoGAP)); - if Song.Resolution <> 4 then Writeln(SongFile, '#RESOLUTION:' + IntToStr(Song.Resolution)); - if Song.NotesGAP <> 0 then Writeln(SongFile, '#NOTESGAP:' + IntToStr(Song.NotesGAP)); - if Song.Start <> 0 then Writeln(SongFile, '#START:' + FloatToStr(Song.Start)); - if Song.Finish <> 0 then Writeln(SongFile, '#END:' + IntToStr(Song.Finish)); - if Relative then Writeln(SongFile, '#RELATIVE:yes'); - - Writeln(SongFile, '#BPM:' + FloatToStr(Song.BPM[0].BPM / 4)); - Writeln(SongFile, '#GAP:' + FloatToStr(Song.GAP)); - - RelativeSubTime := 0; - for B := 1 to High(CurrentSong.BPM) do - Writeln(SongFile, 'B ' + FloatToStr(CurrentSong.BPM[B].StartBeat) + ' ' + FloatToStr(CurrentSong.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) + ' ' + Text; - - - Writeln(SongFile, S); - end; // with - end; // N - - if C < Lines.High then begin // don't write end of last sentence - 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; - Writeln(SongFile, S); - end; + function EncodeToken(const Str: UTF8String): RawByteString; + var + Success: boolean; + begin + Success := EncodeStringUTF8(Str, Result, Song.Encoding); + if (not Success) then + SaveSong := ssrEncodingError; + end; - end; // C + 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; - Writeln(SongFile, 'E'); - CloseFile(SongFile); + end; - Result := true; +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. + diff --git a/Lua/src/base/UFilesystem.pas b/Lua/src/base/UFilesystem.pas new file mode 100644 index 00000000..d4972df5 --- /dev/null +++ b/Lua/src/base/UFilesystem.pas @@ -0,0 +1,692 @@ +{* 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 UFilesystem; + +interface + +{$IFDEF FPC} + {$MODE Delphi} +{$ENDIF} + +{$I switches.inc} + +uses + SysUtils, + Classes, + {$IFDEF MSWINDOWS} + Windows, + TntSysUtils, + {$ENDIF} + UPath; + +type + {$IFDEF MSWINDOWS} + TSytemSearchRec = TSearchRecW; + {$ELSE} + TSytemSearchRec = TSearchRec; + {$ENDIF} + + TFileInfo = record + Time: integer; // timestamp + Size: int64; // file size (byte) + Attr: integer; // file attributes + Name: IPath; // basename with extension + end; + + {** + * Iterates through the search results retrieved by FileFind(). + * Example usage: + * while(Iter.HasNext()) do + * SearchRec := Iter.Next(); + *} + IFileIterator = interface + function HasNext(): boolean; + function Next(): TFileInfo; + end; + + {** + * Wrapper for SysUtils file functions. + * For documentation and examples, check the SysUtils equivalent. + *} + IFileSystem = interface + function ExpandFileName(const FileName: IPath): IPath; + function FileCreate(const FileName: IPath): THandle; + function DirectoryCreate(const Dir: IPath): boolean; + function FileOpen(const FileName: IPath; Mode: longword): THandle; + function FileAge(const FileName: IPath): integer; overload; + function FileAge(const FileName: IPath; out FileDateTime: TDateTime): boolean; overload; + + function DirectoryExists(const Name: IPath): boolean; + + {** + * On Windows: returns true only for files (not directories) + * On Apple/Unix: returns true for all kind of files (even directories) + * @seealso SysUtils.FileExists() + *} + function FileExists(const Name: IPath): boolean; + + function FileGetAttr(const FileName: IPath): Cardinal; + function FileSetAttr(const FileName: IPath; Attr: integer): boolean; + function FileIsReadOnly(const FileName: IPath): boolean; + function FileSetReadOnly(const FileName: IPath; ReadOnly: boolean): boolean; + function FileIsAbsolute(const FileName: IPath): boolean; + function ForceDirectories(const Dir: IPath): boolean; + function RenameFile(const OldName, NewName: IPath): boolean; + function DeleteFile(const FileName: IPath): boolean; + function RemoveDir(const Dir: IPath): boolean; + + {** + * Copies file Source to Target. If FailIfExists is true, the file is not + * copied if it already exists. + * Returns true if the file was successfully copied. + *} + function CopyFile(const Source, Target: IPath; FailIfExists: boolean): boolean; + + function ExtractFileDrive(const FileName: IPath): IPath; + function ExtractFilePath(const FileName: IPath): IPath; + function ExtractFileDir(const FileName: IPath): IPath; + function ExtractFileName(const FileName: IPath): IPath; + function ExtractFileExt(const FileName: IPath): IPath; + function ExtractRelativePath(const BaseName: IPath; const FileName: IPath): IPath; + + function ChangeFileExt(const FileName: IPath; const Extension: IPath): IPath; + + function IncludeTrailingPathDelimiter(const FileName: IPath): IPath; + function ExcludeTrailingPathDelimiter(const FileName: IPath): IPath; + + {** + * Searches for a file with filename Name in the directories given in DirList. + *} + function FileSearch(const Name: IPath; DirList: array of IPath): IPath; + + {** + * More convenient version of FindFirst/Next/Close with iterator support. + *} + function FileFind(const FilePattern: IPath; Attr: integer): IFileIterator; + + {** + * Old style search functions. Use FileFind() instead. + *} + function FindFirst(const FilePattern: IPath; Attr: integer; var F: TSytemSearchRec): integer; + function FindNext(var F: TSytemSearchRec): integer; + procedure FindClose(var F: TSytemSearchRec); + + function GetCurrentDir: IPath; + function SetCurrentDir(const Dir: IPath): boolean; + + {** + * Returns true if the filesystem is case-sensitive. + *} + function IsCaseSensitive(): boolean; + end; + + function FileSystem(): IFileSystem; + +implementation + +type + TFileSystemImpl = class(TInterfacedObject, IFileSystem) + public + function ExpandFileName(const FileName: IPath): IPath; + function FileCreate(const FileName: IPath): THandle; + function DirectoryCreate(const Dir: IPath): boolean; + function FileOpen(const FileName: IPath; Mode: longword): THandle; + function FileAge(const FileName: IPath): integer; overload; + function FileAge(const FileName: IPath; out FileDateTime: TDateTime): boolean; overload; + function DirectoryExists(const Name: IPath): boolean; + function FileExists(const Name: IPath): boolean; + function FileGetAttr(const FileName: IPath): Cardinal; + function FileSetAttr(const FileName: IPath; Attr: integer): boolean; + function FileIsReadOnly(const FileName: IPath): boolean; + function FileSetReadOnly(const FileName: IPath; ReadOnly: boolean): boolean; + function FileIsAbsolute(const FileName: IPath): boolean; + function ForceDirectories(const Dir: IPath): boolean; + function RenameFile(const OldName, NewName: IPath): boolean; + function DeleteFile(const FileName: IPath): boolean; + function RemoveDir(const Dir: IPath): boolean; + function CopyFile(const Source, Target: IPath; FailIfExists: boolean): boolean; + + function ExtractFileDrive(const FileName: IPath): IPath; + function ExtractFilePath(const FileName: IPath): IPath; + function ExtractFileDir(const FileName: IPath): IPath; + function ExtractFileName(const FileName: IPath): IPath; + function ExtractFileExt(const FileName: IPath): IPath; + function ExtractRelativePath(const BaseName: IPath; const FileName: IPath): IPath; + function ChangeFileExt(const FileName: IPath; const Extension: IPath): IPath; + function IncludeTrailingPathDelimiter(const FileName: IPath): IPath; + function ExcludeTrailingPathDelimiter(const FileName: IPath): IPath; + + function FileSearch(const Name: IPath; DirList: array of IPath): IPath; + function FileFind(const FilePattern: IPath; Attr: integer): IFileIterator; + + function FindFirst(const FilePattern: IPath; Attr: integer; var F: TSytemSearchRec): integer; + function FindNext(var F: TSytemSearchRec): integer; + procedure FindClose(var F: TSytemSearchRec); + + function GetCurrentDir: IPath; + function SetCurrentDir(const Dir: IPath): boolean; + + function IsCaseSensitive(): boolean; + end; + + TFileIterator = class(TInterfacedObject, IFileIterator) + private + fHasNext: boolean; + fSearchRec: TSytemSearchRec; + public + constructor Create(const FilePattern: IPath; Attr: integer); + destructor Destroy(); override; + + function HasNext(): boolean; + function Next(): TFileInfo; + end; + + +var + FileSystem_Singleton: IFileSystem; + +function FileSystem(): IFileSystem; +begin + Result := FileSystem_Singleton; +end; + +function TFileSystemImpl.FileFind(const FilePattern: IPath; Attr: integer): IFileIterator; +begin + Result := TFileIterator.Create(FilePattern, Attr); +end; + +function TFileSystemImpl.IsCaseSensitive(): boolean; +begin + // Windows and Mac OS X do not have case sensitive file systems + {$IF Defined(MSWINDOWS) or Defined(DARWIN)} + Result := false; + {$ELSE} + Result := true; + {$IFEND} +end; + +function TFileSystemImpl.FileIsAbsolute(const FileName: IPath): boolean; +var + NameStr: UTF8String; +begin + Result := true; + NameStr := FileName.ToUTF8(); + + {$IFDEF MSWINDOWS} + // check if drive is given 'C:...' + if (FileName.GetDrive().ToUTF8 <> '') then + Exit; + // check if path starts with '\\' + if (Length(NameStr) >= 2) and + (NameStr[1] = PathDelim) and (NameStr[2] = PathDelim) then + Exit; + {$ELSE} // Unix based systems + // check if root dir given '/...' + if (Length(NameStr) >= 1) and (NameStr[1] = PathDelim) then + Exit; + {$ENDIF} + + Result := false; +end; + +{$IFDEF MSWINDOWS} + +function TFileSystemImpl.ExpandFileName(const FileName: IPath): IPath; +begin + Result := Path(WideExpandFileName(FileName.ToWide())); +end; + +function TFileSystemImpl.FileCreate(const FileName: IPath): THandle; +begin + Result := WideFileCreate(FileName.ToWide()); +end; + +function TFileSystemImpl.DirectoryCreate(const Dir: IPath): boolean; +begin + Result := WideCreateDir(Dir.ToWide()); +end; + +function TFileSystemImpl.FileOpen(const FileName: IPath; Mode: longword): THandle; +begin + Result := WideFileOpen(FileName.ToWide(), Mode); +end; + +function TFileSystemImpl.FileAge(const FileName: IPath): integer; +begin + Result := WideFileAge(FileName.ToWide()); +end; + +function TFileSystemImpl.FileAge(const FileName: IPath; out FileDateTime: TDateTime): boolean; +begin + Result := WideFileAge(FileName.ToWide(), FileDateTime); +end; + +function TFileSystemImpl.DirectoryExists(const Name: IPath): boolean; +begin + Result := WideDirectoryExists(Name.ToWide()); +end; + +function TFileSystemImpl.FileExists(const Name: IPath): boolean; +begin + Result := WideFileExists(Name.ToWide()); +end; + +function TFileSystemImpl.FileGetAttr(const FileName: IPath): Cardinal; +begin + Result := WideFileGetAttr(FileName.ToWide()); +end; + +function TFileSystemImpl.FileSetAttr(const FileName: IPath; Attr: integer): boolean; +begin + Result := WideFileSetAttr(FileName.ToWide(), Attr); +end; + +function TFileSystemImpl.FileIsReadOnly(const FileName: IPath): boolean; +begin + Result := WideFileIsReadOnly(FileName.ToWide()); +end; + +function TFileSystemImpl.FileSetReadOnly(const FileName: IPath; ReadOnly: boolean): boolean; +begin + Result := WideFileSetReadOnly(FileName.ToWide(), ReadOnly); +end; + +function TFileSystemImpl.ForceDirectories(const Dir: IPath): boolean; +begin + Result := WideForceDirectories(Dir.ToWide()); +end; + +function TFileSystemImpl.FileSearch(const Name: IPath; DirList: array of IPath): IPath; +var + I: integer; + DirListStr: WideString; +begin + DirListStr := ''; + for I := 0 to High(DirList) do + begin + if (I > 0) then + DirListStr := DirListStr + PathSep; + DirListStr := DirListStr + DirList[I].ToWide(); + end; + Result := Path(WideFileSearch(Name.ToWide(), DirListStr)); +end; + +function TFileSystemImpl.RenameFile(const OldName, NewName: IPath): boolean; +begin + Result := WideRenameFile(OldName.ToWide(), NewName.ToWide()); +end; + +function TFileSystemImpl.DeleteFile(const FileName: IPath): boolean; +begin + Result := WideDeleteFile(FileName.ToWide()); +end; + +function TFileSystemImpl.RemoveDir(const Dir: IPath): boolean; +begin + Result := WideRemoveDir(Dir.ToWide()); +end; + +function TFileSystemImpl.CopyFile(const Source, Target: IPath; FailIfExists: boolean): boolean; +begin + Result := WideCopyFile(Source.ToWide(), Target.ToWide(), FailIfExists); +end; + +function TFileSystemImpl.ExtractFileDrive(const FileName: IPath): IPath; +begin + Result := Path(WideExtractFileDrive(FileName.ToWide())); +end; + +function TFileSystemImpl.ExtractFilePath(const FileName: IPath): IPath; +begin + Result := Path(WideExtractFilePath(FileName.ToWide())); +end; + +function TFileSystemImpl.ExtractFileDir(const FileName: IPath): IPath; +begin + Result := Path(WideExtractFileDir(FileName.ToWide())); +end; + +function TFileSystemImpl.ExtractFileName(const FileName: IPath): IPath; +begin + Result := Path(WideExtractFileName(FileName.ToWide())); +end; + +function TFileSystemImpl.ExtractFileExt(const FileName: IPath): IPath; +begin + Result := Path(WideExtractFileExt(FileName.ToWide())); +end; + +function TFileSystemImpl.ExtractRelativePath(const BaseName: IPath; const FileName: IPath): IPath; +begin + Result := Path(WideExtractRelativePath(BaseName.ToWide(), FileName.ToWide())); +end; + +function TFileSystemImpl.ChangeFileExt(const FileName: IPath; const Extension: IPath): IPath; +begin + Result := Path(WideChangeFileExt(FileName.ToWide(), Extension.ToWide())); +end; + +function TFileSystemImpl.IncludeTrailingPathDelimiter(const FileName: IPath): IPath; +begin + Result := Path(WideIncludeTrailingPathDelimiter(FileName.ToWide())); +end; + +function TFileSystemImpl.ExcludeTrailingPathDelimiter(const FileName: IPath): IPath; +begin + Result := Path(WideExcludeTrailingPathDelimiter(FileName.ToWide())); +end; + +function TFileSystemImpl.FindFirst(const FilePattern: IPath; Attr: integer; var F: TSytemSearchRec): integer; +begin + Result := WideFindFirst(FilePattern.ToWide(), Attr, F); +end; + +function TFileSystemImpl.FindNext(var F: TSytemSearchRec): integer; +begin + Result := WideFindNext(F); +end; + +procedure TFileSystemImpl.FindClose(var F: TSytemSearchRec); +begin + WideFindClose(F); +end; + +function TFileSystemImpl.GetCurrentDir: IPath; +begin + Result := Path(WideGetCurrentDir()); +end; + +function TFileSystemImpl.SetCurrentDir(const Dir: IPath): boolean; +begin + Result := WideSetCurrentDir(Dir.ToWide()); +end; + +{$ELSE} // UNIX + +function TFileSystemImpl.ExpandFileName(const FileName: IPath): IPath; +begin + Result := Path(SysUtils.ExpandFileName(FileName.ToNative())); +end; + +function TFileSystemImpl.FileCreate(const FileName: IPath): THandle; +begin + Result := SysUtils.FileCreate(FileName.ToNative()); +end; + +function TFileSystemImpl.DirectoryCreate(const Dir: IPath): boolean; +begin + Result := SysUtils.CreateDir(Dir.ToNative()); +end; + +function TFileSystemImpl.FileOpen(const FileName: IPath; Mode: longword): THandle; +begin + Result := SysUtils.FileOpen(FileName.ToNative(), Mode); +end; + +function TFileSystemImpl.FileAge(const FileName: IPath): integer; +begin + Result := SysUtils.FileAge(FileName.ToNative()); +end; + +function TFileSystemImpl.FileAge(const FileName: IPath; out FileDateTime: TDateTime): boolean; +var + FileDate: integer; +begin + FileDate := SysUtils.FileAge(FileName.ToNative()); + Result := (FileDate <> -1); + if (Result) then + FileDateTime := FileDateToDateTime(FileDate); +end; + +function TFileSystemImpl.DirectoryExists(const Name: IPath): boolean; +begin + Result := SysUtils.DirectoryExists(Name.ToNative()); +end; + +function TFileSystemImpl.FileExists(const Name: IPath): boolean; +begin + Result := SysUtils.FileExists(Name.ToNative()); +end; + +function TFileSystemImpl.FileGetAttr(const FileName: IPath): Cardinal; +begin + Result := SysUtils.FileGetAttr(FileName.ToNative()); +end; + +function TFileSystemImpl.FileSetAttr(const FileName: IPath; Attr: integer): boolean; +begin + Result := (SysUtils.FileSetAttr(FileName.ToNative(), Attr) = 0); +end; + +function TFileSystemImpl.FileIsReadOnly(const FileName: IPath): boolean; +begin + Result := SysUtils.FileIsReadOnly(FileName.ToNative()); +end; + +function TFileSystemImpl.FileSetReadOnly(const FileName: IPath; ReadOnly: boolean): boolean; +begin + Result := (SysUtils.FileSetAttr(FileName.ToNative(), faReadOnly) = 0); +end; + +function TFileSystemImpl.ForceDirectories(const Dir: IPath): boolean; +begin + Result := SysUtils.ForceDirectories(Dir.ToNative()); +end; + +function TFileSystemImpl.FileSearch(const Name: IPath; DirList: array of IPath): IPath; +var + I: integer; + DirListStr: AnsiString; +begin + DirListStr := ''; + for I := 0 to High(DirList) do + begin + if (I > 0) then + DirListStr := DirListStr + PathSep; + DirListStr := DirListStr + DirList[I].ToNative(); + end; + Result := Path(SysUtils.FileSearch(Name.ToNative(), DirListStr)); +end; + +function TFileSystemImpl.RenameFile(const OldName, NewName: IPath): boolean; +begin + Result := SysUtils.RenameFile(OldName.ToNative(), NewName.ToNative()); +end; + +function TFileSystemImpl.DeleteFile(const FileName: IPath): boolean; +begin + Result := SysUtils.DeleteFile(FileName.ToNative()); +end; + +function TFileSystemImpl.RemoveDir(const Dir: IPath): boolean; +begin + Result := SysUtils.RemoveDir(Dir.ToNative()); +end; + +function TFileSystemImpl.CopyFile(const Source, Target: IPath; FailIfExists: boolean): boolean; +const + COPY_BUFFER_SIZE = 4096; // a good tradeoff between speed and memory consumption +var + SourceFile, TargetFile: TFileStream; + FileCopyBuffer: array [0..COPY_BUFFER_SIZE-1] of byte; // temporary copy-buffer. + NumberOfBytes: integer; // number of bytes read from SourceFile +begin + Result := false; + SourceFile := nil; + TargetFile := nil; + + // if overwrite is disabled return if the target file already exists + if (FailIfExists and FileExists(Target)) then + Exit; + + try + try + // open source and target file (might throw an exception on error) + SourceFile := TFileStream.Create(Source.ToNative(), fmOpenRead); + TargetFile := TFileStream.Create(Target.ToNative(), fmCreate or fmOpenWrite); + + while true do + begin + // read a block from the source file and check for errors or EOF + NumberOfBytes := SourceFile.Read(FileCopyBuffer, SizeOf(FileCopyBuffer)); + if (NumberOfBytes <= 0) then + Break; + // write block to target file and check if everything was written + if (TargetFile.Write(FileCopyBuffer, NumberOfBytes) <> NumberOfBytes) then + Exit; + end; + except + Exit; + end; + finally + SourceFile.Free; + TargetFile.Free; + end; + + Result := true; +end; + +function TFileSystemImpl.ExtractFileDrive(const FileName: IPath): IPath; +begin + Result := Path(SysUtils.ExtractFileDrive(FileName.ToNative())); +end; + +function TFileSystemImpl.ExtractFilePath(const FileName: IPath): IPath; +begin + Result := Path(SysUtils.ExtractFilePath(FileName.ToNative())); +end; + +function TFileSystemImpl.ExtractFileDir(const FileName: IPath): IPath; +begin + Result := Path(SysUtils.ExtractFileDir(FileName.ToNative())); +end; + +function TFileSystemImpl.ExtractFileName(const FileName: IPath): IPath; +begin + Result := Path(SysUtils.ExtractFileName(FileName.ToNative())); +end; + +function TFileSystemImpl.ExtractFileExt(const FileName: IPath): IPath; +begin + Result := Path(SysUtils.ExtractFileExt(FileName.ToNative())); +end; + +function TFileSystemImpl.ExtractRelativePath(const BaseName: IPath; const FileName: IPath): IPath; +begin + Result := Path(SysUtils.ExtractRelativePath(BaseName.ToNative(), FileName.ToNative())); +end; + +function TFileSystemImpl.ChangeFileExt(const FileName: IPath; const Extension: IPath): IPath; +begin + Result := Path(SysUtils.ChangeFileExt(FileName.ToNative(), Extension.ToNative())); +end; + +function TFileSystemImpl.IncludeTrailingPathDelimiter(const FileName: IPath): IPath; +begin + Result := Path(SysUtils.IncludeTrailingPathDelimiter(FileName.ToNative())); +end; + +function TFileSystemImpl.ExcludeTrailingPathDelimiter(const FileName: IPath): IPath; +begin + Result := Path(SysUtils.ExcludeTrailingPathDelimiter(FileName.ToNative())); +end; + +function TFileSystemImpl.FindFirst(const FilePattern: IPath; Attr: integer; var F: TSytemSearchRec): integer; +begin + Result := SysUtils.FindFirst(FilePattern.ToNative(), Attr, F); +end; + +function TFileSystemImpl.FindNext(var F: TSytemSearchRec): integer; +begin + Result := SysUtils.FindNext(F); +end; + +procedure TFileSystemImpl.FindClose(var F: TSytemSearchRec); +begin + SysUtils.FindClose(F); +end; + +function TFileSystemImpl.GetCurrentDir: IPath; +begin + Result := Path(SysUtils.GetCurrentDir()); +end; + +function TFileSystemImpl.SetCurrentDir(const Dir: IPath): boolean; +begin + Result := SysUtils.SetCurrentDir(Dir.ToNative()); +end; + +{$ENDIF} + + +{ TFileIterator } + +constructor TFileIterator.Create(const FilePattern: IPath; Attr: integer); +begin + inherited Create(); + fHasNext := (FileSystem.FindFirst(FilePattern, Attr, fSearchRec) = 0); +end; + +destructor TFileIterator.Destroy(); +begin + FileSystem.FindClose(fSearchRec); + inherited; +end; + +function TFileIterator.HasNext(): boolean; +begin + Result := fHasNext; +end; + +function TFileIterator.Next(): TFileInfo; +begin + if (not fHasNext) then + begin + // Note: do not use FillChar() on records with ref-counted fields + Result.Time := 0; + Result.Size := 0; + Result.Attr := 0; + Result.Name := nil; + Exit; + end; + + Result.Time := fSearchRec.Time; + Result.Size := fSearchRec.Size; + Result.Attr := fSearchRec.Attr; + Result.Name := Path(fSearchRec.Name); + + // fetch next entry + fHasNext := (FileSystem.FindNext(fSearchRec) = 0); +end; + + +initialization + FileSystem_Singleton := TFileSystemImpl.Create; + +finalization + FileSystem_Singleton := nil; + +end. diff --git a/Lua/src/base/UFont.pas b/Lua/src/base/UFont.pas index a72bca21..191e74d2 100644 --- a/Lua/src/base/UFont.pas +++ b/Lua/src/base/UFont.pas @@ -47,12 +47,14 @@ uses glext, glu, sdl, + Math, + Classes, + SysUtils, + UUnicodeUtils, {$IFDEF BITMAP_FONT} UTexture, {$ENDIF} - Math, - Classes, - SysUtils; + UPath; type @@ -60,7 +62,7 @@ type TGLubyteArray = array[0 .. (MaxInt div SizeOf(GLubyte))-1] of GLubyte; TGLubyteDynArray = array of GLubyte; - TWideStringArray = array of WideString; + TUCS4StringArray = array of UCS4String; TGLColor = packed record case byte of @@ -126,34 +128,34 @@ type {** * Splits lines in Text seperated by newline (char-code #13). - * @param Text UTF-8 encoded string - * @param Lines splitted WideString lines + * @param Text UCS-4 encoded string + * @param Lines splitted UCS4String lines *} - procedure SplitLines(const Text: UTF8String; var Lines: TWideStringArray); + procedure SplitLines(const Text: UCS4String; var Lines: TUCS4StringArray); {** - * Print an array of WideStrings. Each array-item is a line of text. + * Print an array of UCS4Strings. Each array-item is a line of text. * Lines of text are seperated by the line-spacing. * This is the base function for all text drawing. *} - procedure Print(const Text: TWideStringArray); overload; virtual; + procedure Print(const Text: TUCS4StringArray); overload; virtual; {** * Draws an underline. *} - procedure DrawUnderline(const Text: WideString); virtual; + procedure DrawUnderline(const Text: UCS4String); virtual; {** * Renders (one) line of text. *} - procedure Render(const Text: WideString); virtual; abstract; + procedure Render(const Text: UCS4String); virtual; abstract; {** * Returns the bounds of text-lines contained in Text. * @param(Advance if true the right bound is set to the advance instead * of the minimal right bound.) *} - function BBox(const Text: TWideStringArray; Advance: boolean): TBoundsDbl; overload; virtual; abstract; + function BBox(const Text: TUCS4StringArray; Advance: boolean): TBoundsDbl; overload; virtual; abstract; {** * Resets all user settings to default values. @@ -188,9 +190,11 @@ type {** * Prints a text. *} + procedure Print(const Text: UCS4String); overload; + {** UTF-16 version of @link(Print) } procedure Print(const Text: WideString); overload; {** UTF-8 version of @link(Print) } - procedure Print(const Text: string); overload; + procedure Print(const Text: UTF8String); overload; {** * Calculates the bounding box (width and height) around Text. @@ -203,6 +207,8 @@ type * bigger than the text's width as it additionally contains the advance * and glyph-spacing of the last character. *} + function BBox(const Text: UCS4String; Advance: boolean = true): TBoundsDbl; overload; + {** UTF-16 version of @link(BBox) } function BBox(const Text: WideString; Advance: boolean = true): TBoundsDbl; overload; {** UTF-8 version of @link(BBox) } function BBox(const Text: UTF8String; Advance: boolean = true): TBoundsDbl; overload; @@ -249,9 +255,9 @@ type /// Mipmap fonts (size[level+1] = size[level]/2) fMipmapFonts: array[0..cMaxMipmapLevel] of TFont; - procedure Render(const Text: WideString); override; - procedure Print(const Text: TWideStringArray); override; - function BBox(const Text: TWideStringArray; Advance: boolean): TBoundsDbl; override; + procedure Render(const Text: UCS4String); override; + procedure Print(const Text: TUCS4StringArray); override; + function BBox(const Text: TUCS4StringArray; Advance: boolean): TBoundsDbl; override; {** * Callback called for creation of each mipmap font. @@ -322,7 +328,7 @@ type {** * Table for storage of max. 256 glyphs. - * Used for the second cache level. Indexed by the LSB of the WideChar + * Used for the second cache level. Indexed by the LSB of the UCS4Char * char-code. *} PGlyphTable = ^TGlyphTable; @@ -332,7 +338,7 @@ type * Cache for glyphs of a single font. * The cached glyphs are stored inside a hash-list. * Hashing is performed in two steps: - * 1. the least significant byte (LSB) of the WideChar character code + * 1. the least significant byte (LSB) of the UCS4Char character code * is removed (shr 8) and the result (we call it BaseCode here) looked up in * the hash-list. * 2. Each entry of the hash-list contains a table with max. 256 entries. @@ -359,22 +365,22 @@ type * Add glyph Glyph with char-code ch to the cache. * @returns @true on success, @false otherwise *} - function AddGlyph(ch: WideChar; const Glyph: TGlyph): boolean; + function AddGlyph(ch: UCS4Char; const Glyph: TGlyph): boolean; {** * Removes the glyph with char-code ch from the cache. *} - procedure DeleteGlyph(ch: WideChar); + procedure DeleteGlyph(ch: UCS4Char); {** * Removes the glyph with char-code ch from the cache. *} - function GetGlyph(ch: WideChar): TGlyph; + function GetGlyph(ch: UCS4Char): TGlyph; {** * Checks if a glyph with char-code ch is cached. *} - function HasGlyph(ch: WideChar): boolean; + function HasGlyph(ch: UCS4Char): boolean; {** * Remove and free all cached glyphs. If KeepBaseSet is set to @@ -408,13 +414,13 @@ type * Retrieves a cached glyph with char-code ch from cache. * If the glyph is not already cached, it is loaded with LoadGlyph(). *} - function GetGlyph(ch: WideChar): TGlyph; + function GetGlyph(ch: UCS4Char): TGlyph; {** * Callback to create (load) a glyph with char-code ch. * Implemented by subclasses. *} - function LoadGlyph(ch: WideChar): TGlyph; virtual; abstract; + function LoadGlyph(ch: UCS4Char): TGlyph; virtual; abstract; public constructor Create(); @@ -436,6 +442,7 @@ type *} TFTGlyph = class(TGlyph) private + fCharCode: UCS4Char; //**< Char code fCharIndex: FT_UInt; //**< Freetype specific char-index (<> char-code) fDisplayList: GLuint; //**< Display-list ID fTexture: GLuint; //**< Texture ID @@ -458,7 +465,7 @@ type * The bitmap must be 2* pixels wider and higher than the * original glyph's bitmap with the latter centered in it. *} - procedure Extrude(var TexBuffer: TGLubyteDynArray; Outset: single); + procedure StrokeBorder(var Glyph: FT_Glyph); {** * Creates an OpenGL texture (and display list) for the glyph. @@ -477,7 +484,7 @@ type * Creates a glyph with char-code ch from font Font. * @param LoadFlags flags passed to FT_Load_Glyph() *} - constructor Create(Font: TFTFont; ch: WideChar; Outset: single; + constructor Create(Font: TFTFont; ch: UCS4Char; Outset: single; LoadFlags: FT_Int32); destructor Destroy(); override; @@ -490,6 +497,8 @@ type property CharIndex: FT_UInt read fCharIndex; end; + TFontPart = ( fpNone, fpInner, fpOutline ); + {** * Freetype font class. *} @@ -498,19 +507,20 @@ type procedure ResetIntern(); protected - fFilename: string; //**< filename of the font-file + fFilename: IPath; //**< filename of the font-file fSize: integer; //**< Font base size (in pixels) fOutset: single; //**< size of outset extrusion (in pixels) fFace: FT_Face; //**< Holds the height of the font fLoadFlags: FT_Int32; //**< FT glpyh load-flags fFontUnitScale: TPositionDbl; //**< FT font-units to pixel ratio fUseDisplayLists: boolean; //**< true: use display-lists, false: direct drawing + fPart: TFontPart; //**< indicates the part of an outline font {** @seealso TCachedFont.LoadGlyph } - function LoadGlyph(ch: WideChar): TGlyph; override; + function LoadGlyph(ch: UCS4Char): TGlyph; override; - procedure Render(const Text: WideString); override; - function BBox(const Text: TWideStringArray; Advance: boolean): TBoundsDbl; override; + procedure Render(const Text: UCS4String); override; + function BBox(const Text: TUCS4StringArray; Advance: boolean): TBoundsDbl; override; function GetHeight(): single; override; function GetAscender(): single; override; @@ -528,7 +538,7 @@ type * @param LoadFlags flags passed to FT_Load_Glyph() * @raises Exception if the font-file could not be loaded *} - constructor Create(const Filename: string; + constructor Create(const Filename: IPath; Size: integer; Outset: single = 0.0; LoadFlags: FT_Int32 = FT_LOAD_DEFAULT); @@ -558,7 +568,7 @@ type * The extrusion in pixels is Size*OutsetAmount * (0.0 -> no extrusion, 0.1 -> 10%). *} - constructor Create(const Filename: string; + constructor Create(const Filename: IPath; Size: integer; OutsetAmount: single = 0.0; UseMipmaps: boolean = true); @@ -576,7 +586,7 @@ type *} TFTOutlineFont = class(TFont) private - fFilename: string; + fFilename: IPath; fSize: integer; fOutset: single; fInnerFont, fOutlineFont: TFTFont; @@ -585,9 +595,9 @@ type procedure ResetIntern(); protected - procedure DrawUnderline(const Text: WideString); override; - procedure Render(const Text: WideString); override; - function BBox(const Text: TWideStringArray; Advance: boolean): TBoundsDbl; override; + procedure DrawUnderline(const Text: UCS4String); override; + procedure Render(const Text: UCS4String); override; + function BBox(const Text: TUCS4StringArray; Advance: boolean): TBoundsDbl; override; function GetHeight(): single; override; function GetAscender(): single; override; @@ -603,7 +613,7 @@ type procedure SetReflectionPass(Enable: boolean); override; public - constructor Create(const Filename: string; + constructor Create(const Filename: IPath; Size: integer; Outset: single; LoadFlags: FT_Int32 = FT_LOAD_DEFAULT); destructor Destroy; override; @@ -637,7 +647,7 @@ type function CreateMipmap(Level: integer; Scale: single): TFont; override; public - constructor Create(const Filename: string; + constructor Create(const Filename: IPath; Size: integer; OutsetAmount: single; UseMipmaps: boolean = true); @@ -672,18 +682,18 @@ type procedure ResetIntern(); - procedure RenderChar(ch: WideChar; var AdvanceX: real); + procedure RenderChar(ch: UCS4Char; var AdvanceX: real); {** * Load font widths from an info file. * @param InfoFile the name of the info (.dat) file * @raises Exception if the file is corrupted *} - procedure LoadFontInfo(const InfoFile: string); + procedure LoadFontInfo(const InfoFile: IPath); protected - procedure Render(const Text: WideString); override; - function BBox(const Text: TWideStringArray; Advance: boolean): TBoundsDbl; override; + procedure Render(const Text: UCS4String); override; + function BBox(const Text: TUCS4StringArray; Advance: boolean): TBoundsDbl; override; function GetHeight(): single; override; function GetAscender(): single; override; @@ -699,7 +709,7 @@ type * (y-axis up) and from the lower edge of the glyphs bounding box) * @param(Ascender pixels from baseline to top of highest glyph) *} - constructor Create(const Filename: string; Outline: integer; + constructor Create(const Filename: IPath; Outline: integer; Baseline, Ascender, Descender: integer); destructor Destroy(); override; @@ -801,37 +811,61 @@ begin ResetIntern(); end; -procedure TFont.SplitLines(const Text: UTF8String; var Lines: TWideStringArray); +procedure TFont.SplitLines(const Text: UCS4String; var Lines: TUCS4StringArray); var - LineList: TStringList; - LineIndex: integer; + CharIndex: integer; + LineStart: integer; + LineLength: integer; + EOT: boolean; // End-Of-Text begin - // split lines on newline (there is no WideString version of ExtractStrings) - LineList := TStringList.Create(); - ExtractStrings([#13], [], PChar(Text), LineList); + // split lines on newline + SetLength(Lines, 0); + EOT := false; + LineStart := 0; - // create an array of WideStrins from the UTF-8 string-list - SetLength(Lines, LineList.Count); - for LineIndex := 0 to LineList.Count-1 do - Lines[LineIndex] := UTF8Decode(LineList[LineIndex]); - LineList.Free(); + for CharIndex := 0 to High(Text) do + begin + // check for end of text (UCS4Strings are zero-terminated) + if (CharIndex = High(Text)) then + EOT := true; + + // check for newline (carriage return (#13)) or end of text + if (Text[CharIndex] = 13) or EOT then + begin + LineLength := CharIndex - LineStart; + // check if last character was a newline + if (EOT and (LineLength = 0)) then + Break; + + // copy line (even if LineLength is 0) + SetLength(Lines, Length(Lines)+1); + Lines[High(Lines)] := UCS4Copy(Text, LineStart, LineLength); + + LineStart := CharIndex+1; + end; + end; end; -function TFont.BBox(const Text: UTF8String; Advance: boolean): TBoundsDbl; +function TFont.BBox(const Text: UCS4String; Advance: boolean): TBoundsDbl; var - LineArray: TWideStringArray; + LineArray: TUCS4StringArray; begin SplitLines(Text, LineArray); Result := BBox(LineArray, Advance); SetLength(LineArray, 0); end; +function TFont.BBox(const Text: UTF8String; Advance: boolean): TBoundsDbl; +begin + Result := BBox(UTF8Decode(Text), Advance); +end; + function TFont.BBox(const Text: WideString; Advance: boolean): TBoundsDbl; begin - Result := BBox(UTF8Encode(Text), Advance); + Result := BBox(WideStringToUCS4String(Text), Advance); end; -procedure TFont.Print(const Text: TWideStringArray); +procedure TFont.Print(const Text: TUCS4StringArray); var LineIndex: integer; begin @@ -912,21 +946,26 @@ begin glPopAttrib(); end; -procedure TFont.Print(const Text: string); +procedure TFont.Print(const Text: UCS4String); var - LineArray: TWideStringArray; + LineArray: TUCS4StringArray; begin SplitLines(Text, LineArray); Print(LineArray); SetLength(LineArray, 0); end; +procedure TFont.Print(const Text: UTF8String); +begin + Print(UTF8Decode(Text)); +end; + procedure TFont.Print(const Text: WideString); begin - Print(UTF8Encode(Text)); + Print(WideStringToUCS4String(Text)); end; -procedure TFont.DrawUnderline(const Text: WideString); +procedure TFont.DrawUnderline(const Text: UCS4String); var UnderlineY1, UnderlineY2: single; Bounds: TBoundsDbl; @@ -1128,12 +1167,22 @@ begin // projected width ||(x1, y1) - (x2, y1)|| Dist := (WinCoords[0][0] - WinCoords[1][0]); Dist2 := (WinCoords[0][1] - WinCoords[1][1]); - WidthScale := cTestSize / Sqrt(Dist*Dist + Dist2*Dist2); + + WidthScale := 1; + if (Sqrt(Dist*Dist + Dist2*Dist2) <> 0) then + begin + WidthScale := cTestSize / Sqrt(Dist*Dist + Dist2*Dist2); + end; // projected height ||(x1, y1) - (x1, y2)|| Dist := (WinCoords[0][0] - WinCoords[2][0]); Dist2 := (WinCoords[0][1] - WinCoords[2][1]); - HeightScale := cTestSize / Sqrt(Dist*Dist + Dist2*Dist2); + + HeightScale := 1; + if (Sqrt(Dist*Dist + Dist2*Dist2) <> 0) then + begin + HeightScale := cTestSize / Sqrt(Dist*Dist + Dist2*Dist2); + end; //writeln(Format('Scale %f, %f', [WidthScale, HeightScale])); @@ -1194,7 +1243,7 @@ begin glScalef(MipmapScale, MipmapScale, 0); end; -procedure TScalableFont.Print(const Text: TWideStringArray); +procedure TScalableFont.Print(const Text: TUCS4StringArray); begin glPushMatrix(); @@ -1210,12 +1259,12 @@ begin glPopMatrix(); end; -procedure TScalableFont.Render(const Text: WideString); +procedure TScalableFont.Render(const Text: UCS4String); begin Assert(false, 'Unused TScalableFont.Render() was called'); end; -function TScalableFont.BBox(const Text: TWideStringArray; Advance: boolean): TBoundsDbl; +function TScalableFont.BBox(const Text: TUCS4StringArray; Advance: boolean): TBoundsDbl; begin Result := fBaseFont.BBox(Text, Advance); Result.Left := Result.Left * fScale * fAspect; @@ -1287,7 +1336,7 @@ var Level: integer; begin for Level := 0 to High(fMipmapFonts) do - if (fMipmapFonts[Level] <> nil) then + if ((fMipmapFonts[Level] <> nil) AND (GetMipmapScale(Level) > 0)) then fMipmapFonts[Level].SetReflectionSpacing(Spacing / GetMipmapScale(Level)); end; @@ -1346,7 +1395,7 @@ begin inherited; end; -function TCachedFont.GetGlyph(ch: WideChar): TGlyph; +function TCachedFont.GetGlyph(ch: UCS4Char): TGlyph; begin Result := fCache.GetGlyph(ch); if (Result = nil) then @@ -1368,11 +1417,11 @@ end; *} constructor TFTFont.Create( - const Filename: string; + const Filename: IPath; Size: integer; Outset: single; LoadFlags: FT_Int32); var - i: WideChar; + ch: UCS4Char; begin inherited Create(); @@ -1381,10 +1430,11 @@ begin fOutset := Outset; fLoadFlags := LoadFlags; fUseDisplayLists := true; + fPart := fpNone; // load font information - if (FT_New_Face(TFreeType.GetLibrary(), PChar(Filename), 0, fFace) <> 0) then - raise Exception.Create('FT_New_Face: Could not load font ''' + Filename + ''''); + if (FT_New_Face(TFreeType.GetLibrary(), PChar(Filename.ToNative), 0, fFace) <> 0) then + raise Exception.Create('FT_New_Face: Could not load font ''' + Filename.ToNative + ''''); // support scalable fonts only if (not FT_IS_SCALABLE(fFace)) then @@ -1400,8 +1450,8 @@ begin ResetIntern(); // pre-cache some commonly used glyphs (' ' - '~') - for i := #32 to #126 do - fCache.AddGlyph(i, TFTGlyph.Create(Self, i, Outset, LoadFlags)); + for ch := 32 to 126 do + fCache.AddGlyph(ch, TFTGlyph.Create(Self, ch, Outset, LoadFlags)); end; destructor TFTFont.Destroy(); @@ -1424,15 +1474,15 @@ begin ResetIntern(); end; -function TFTFont.LoadGlyph(ch: WideChar): TGlyph; +function TFTFont.LoadGlyph(ch: UCS4Char): TGlyph; begin Result := TFTGlyph.Create(Self, ch, Outset, fLoadFlags); end; -function TFTFont.BBox(const Text: TWideStringArray; Advance: boolean): TBoundsDbl; +function TFTFont.BBox(const Text: TUCS4StringArray; Advance: boolean): TBoundsDbl; var Glyph, PrevGlyph: TFTGlyph; - TextLine: WideString; + TextLine: UCS4String; LineYOffset: single; LineIndex, CharIndex: integer; LineBounds: TBoundsDbl; @@ -1462,7 +1512,7 @@ begin LineBounds.Top := 0; // for each glyph image, compute its bounding box - for CharIndex := 1 to Length(TextLine) do + for CharIndex := 0 to LengthUCS4(TextLine)-1 do begin Glyph := TFTGlyph(GetGlyph(TextLine[CharIndex])); if (Glyph <> nil) then @@ -1480,9 +1530,9 @@ begin LineBounds.Left := LineBounds.Right + Glyph.Bounds.Left; // update right bound - if (CharIndex < Length(TextLine)) or // not the last character - (TextLine[CharIndex] = ' ') or // on space char (Bounds.Right = 0) - Advance then // or in advance mode + if (CharIndex < LengthUCS4(TextLine)-1) or // not the last character + (TextLine[CharIndex] = Ord(' ')) or // on space char (Bounds.Right = 0) + Advance then // or in advance mode begin // add advance and glyph spacing LineBounds.Right := LineBounds.Right + Glyph.Advance.x + GlyphSpacing @@ -1534,13 +1584,13 @@ begin end; // if left or bottom bound was not set, set them to 0 - if (Result.Left = Infinity) then + if (IsInfinite(Result.Left)) then Result.Left := 0.0; - if (Result.Bottom = Infinity) then + if (IsInfinite(Result.Bottom)) then Result.Bottom := 0.0; end; -procedure TFTFont.Render(const Text: WideString); +procedure TFTFont.Render(const Text: UCS4String); var CharIndex: integer; Glyph, PrevGlyph: TFTGlyph; @@ -1550,7 +1600,7 @@ begin PrevGlyph := nil; // draw current line - for CharIndex := 1 to Length(Text) do + for CharIndex := 0 to LengthUCS4(Text)-1 do begin Glyph := TFTGlyph(GetGlyph(Text[CharIndex])); if (Assigned(Glyph)) then @@ -1606,7 +1656,7 @@ end; * TFTScalableFont *} -constructor TFTScalableFont.Create(const Filename: string; +constructor TFTScalableFont.Create(const Filename: IPath; Size: integer; OutsetAmount: single; UseMipmaps: boolean); var @@ -1662,7 +1712,7 @@ end; *} constructor TFTOutlineFont.Create( - const Filename: string; + const Filename: IPath; Size: integer; Outset: single; LoadFlags: FT_Int32); begin @@ -1673,7 +1723,9 @@ begin fOutset := Outset; fInnerFont := TFTFont.Create(Filename, Size, 0.0, LoadFlags); + fInnerFont.fPart := fpInner; fOutlineFont := TFTFont.Create(Filename, Size, Outset, LoadFlags); + fOutlineFont.fPart := fpOutline; ResetIntern(); end; @@ -1705,7 +1757,7 @@ begin ResetIntern(); end; -procedure TFTOutlineFont.DrawUnderline(const Text: WideString); +procedure TFTOutlineFont.DrawUnderline(const Text: UCS4String); var CurrentColor: TGLColor; OutlineColor: TGLColor; @@ -1730,7 +1782,7 @@ begin glPopMatrix(); end; -procedure TFTOutlineFont.Render(const Text: WideString); +procedure TFTOutlineFont.Render(const Text: UCS4String); var CurrentColor: TGLColor; OutlineColor: TGLColor; @@ -1770,7 +1822,7 @@ begin fInnerFont.FlushCache(KeepBaseSet); end; -function TFTOutlineFont.BBox(const Text: TWideStringArray; Advance: boolean): TBoundsDbl; +function TFTOutlineFont.BBox(const Text: TUCS4StringArray; Advance: boolean): TBoundsDbl; begin Result := fOutlineFont.BBox(Text, Advance); end; @@ -1852,7 +1904,7 @@ end; *} constructor TFTScalableOutlineFont.Create( - const Filename: string; + const Filename: IPath; Size: integer; OutsetAmount: single; UseMipmaps: boolean); var @@ -1935,82 +1987,113 @@ const *} cTexSmoothBorder = 1; -procedure TFTGlyph.Extrude(var TexBuffer: TGLubyteDynArray; Outset: single); +procedure TFTGlyph.StrokeBorder(var Glyph: FT_Glyph); +var + Outline: PFT_Outline; + OuterStroker, InnerStroker: FT_Stroker; + OuterNumPoints, InnerNumPoints, GlyphNumPoints: FT_UInt; + OuterNumContours, InnerNumContours, GlyphNumContours: FT_UInt; + OuterBorder, InnerBorder: FT_StrokerBorder; + OutlineFlags: FT_Int; + UseStencil: boolean; +begin + // It is possible to extrude the borders of a glyph with FT_Glyph_Stroke + // but it will extrude the border to the outside and the inside of a glyph + // although we just want to extrude to the outside. + // FT_Glyph_StrokeBorder extrudes to the outside but also fills the interior + // (this is what we need for bold fonts). + // In both cases the inner font and outline font (border) will overlap. + // Normally this does not matter but it does if alpha blending is active. + // In this case if e.g. the inner color is set to white, the outline to red + // and alpha to 0.5 the inner part will not be white it will be pink. + + InnerStroker := nil; + OuterStroker := nil; + + // If we are to create the interior of an outlined font (fInner = true) + // we have to create two borders: + // - one extruded to the outside by fOutset pixels and + // - one extruded to the inside by almost 0 zero pixels. + // The second one is used as a stencil for the first one, clearing the + // interiour of the glyph. + // The stencil is not needed to create bold fonts. + UseStencil := (fFont.fPart = fpInner); + + Outline := @FT_OutlineGlyph(Glyph).outline; + + OuterBorder := FT_Outline_GetOutsideBorder(Outline); + if (OuterBorder = FT_STROKER_BORDER_LEFT) then + InnerBorder := FT_STROKER_BORDER_RIGHT + else + InnerBorder := FT_STROKER_BORDER_LEFT; + + { extrude outer border } + + if (FT_Stroker_New(Glyph.library_, OuterStroker) <> 0) then + raise Exception.Create('FT_Stroker_New failed!'); + FT_Stroker_Set( + OuterStroker, + Round(fOutset * 64), + FT_STROKER_LINECAP_ROUND, + FT_STROKER_LINEJOIN_BEVEL, + 0); + + // similar to FT_Glyph_StrokeBorder(inner = FT_FALSE) but it is possible to + // use FT_Stroker_ExportBorder() afterwards to combine inner and outer borders + if (FT_Stroker_ParseOutline(OuterStroker, Outline, FT_FALSE) <> 0) then + raise Exception.Create('FT_Stroker_ParseOutline failed!'); - procedure SetToMax(var Val1: GLubyte; Val2: GLubyte); {$IFDEF HasInline}inline;{$ENDIF} + FT_Stroker_GetBorderCounts(OuterStroker, OuterBorder, OuterNumPoints, OuterNumContours); + + { extrude inner border (= stencil) } + + if (UseStencil) then begin - if (Val1 < Val2) then - Val1 := Val2; + if (FT_Stroker_New(Glyph.library_, InnerStroker) <> 0) then + raise Exception.Create('FT_Stroker_New failed!'); + FT_Stroker_Set( + InnerStroker, + 63, // extrude at most one pixel to avoid a black border + FT_STROKER_LINECAP_ROUND, + FT_STROKER_LINEJOIN_BEVEL, + 0); + + if (FT_Stroker_ParseOutline(InnerStroker, Outline, FT_FALSE) <> 0) then + raise Exception.Create('FT_Stroker_ParseOutline failed!'); + + FT_Stroker_GetBorderCounts(InnerStroker, InnerBorder, InnerNumPoints, InnerNumContours); + end else begin + InnerNumPoints := 0; + InnerNumContours := 0; end; -var - I, X, Y: integer; - SrcBuffer,TmpBuffer: TGLubyteDynArray; - TexLine, TexLinePrev, TexLineNext: PGLubyteArray; - SrcLine: PGLubyteArray; - AlphaScale: single; - Value, ValueNeigh, ValueDiag: GLubyte; -const - // square-root of 2 used for diagonal neighbor pixels - cSqrt2 = 1.4142; - // number of ignored pixels on each edge of the bitmap. Consists of: - // - border used for font smoothing and - // - outer (extruded) bitmap pixel (because it is just written but never read) - cBorder = cTexSmoothBorder + 1; -begin - // allocate memory for temporary buffer - SetLength(SrcBuffer, Length(TexBuffer)); - FillChar(SrcBuffer[0], Length(TexBuffer), 0); - - // extrude pixel by pixel - for I := 1 to Ceil(Outset) do - begin - // swap arrays - TmpBuffer := TexBuffer; - TexBuffer := SrcBuffer; - SrcBuffer := TmpBuffer; - - // as long as we add an entire pixel of outset, use a solid color. - // If the fractional part is reached blend, e.g. outline=3.2 -> 3 solid - // pixels and one blended with alpha=0.2. - // For the fractional part I = Ceil(Outset) is always true. - if (I <= Outset) then - AlphaScale := 1 - else - AlphaScale := Outset - Trunc(Outset); - - // copy data to the expanded bitmap. - for Y := cBorder to fTexSize.Height - 2*cBorder do - begin - TexLine := @TexBuffer[Y*fTexSize.Width]; - TexLinePrev := @TexBuffer[(Y-1)*fTexSize.Width]; - TexLineNext := @TexBuffer[(Y+1)*fTexSize.Width]; - SrcLine := @SrcBuffer[Y*fTexSize.Width]; + { combine borders (subtract: OuterBorder - InnerBorder) } - // expand current line's pixels - for X := cBorder to fTexSize.Width - 2*cBorder do - begin - Value := SrcLine[X]; - ValueNeigh := Round(Value * AlphaScale); - ValueDiag := Round(ValueNeigh / cSqrt2); + GlyphNumPoints := InnerNumPoints + OuterNumPoints; + GlyphNumContours := InnerNumContours + OuterNumContours; - SetToMax(TexLine[X], Value); - SetToMax(TexLine[X-1], ValueNeigh); - SetToMax(TexLine[X+1], ValueNeigh); + // save flags before deletion (TODO: set them on the resulting outline) + OutlineFlags := Outline.flags; - SetToMax(TexLinePrev[X], ValueNeigh); - SetToMax(TexLinePrev[X-1], ValueDiag); - SetToMax(TexLinePrev[X+1], ValueDiag); + // resize glyph outline to hold inner and outer border + FT_Outline_Done(Glyph.Library_, Outline); + if (FT_Outline_New(Glyph.Library_, GlyphNumPoints, GlyphNumContours, Outline) <> 0) then + raise Exception.Create('FT_Outline_New failed!'); - SetToMax(TexLineNext[X], ValueNeigh); - SetToMax(TexLineNext[X-1], ValueDiag); - SetToMax(TexLineNext[X+1], ValueDiag); - end; - end; - end; + Outline.n_points := 0; + Outline.n_contours := 0; + + // add points to outline. The inner-border is used as a stencil. + FT_Stroker_ExportBorder(OuterStroker, OuterBorder, Outline); + if (UseStencil) then + FT_Stroker_ExportBorder(InnerStroker, InnerBorder, Outline); + if (FT_Outline_Check(outline) <> 0) then + raise Exception.Create('FT_Stroker_ExportBorder failed!'); - TmpBuffer := nil; - SetLength(SrcBuffer, 0); + if (InnerStroker <> nil) then + FT_Stroker_Done(InnerStroker); + if (OuterStroker <> nil) then + FT_Stroker_Done(OuterStroker); end; procedure TFTGlyph.CreateTexture(LoadFlags: FT_Int32); @@ -2033,6 +2116,9 @@ begin if (FT_Get_Glyph(fFont.Face^.glyph, Glyph) <> 0) then raise Exception.Create('FT_Get_Glyph failed'); + if (fOutset > 0) then + StrokeBorder(Glyph); + // store scaled advance width/height in glyph-object fAdvance.X := fFont.Face^.glyph^.advance.x / 64 + fOutset*2; fAdvance.Y := fFont.Face^.glyph^.advance.y / 64 + fOutset*2; @@ -2114,9 +2200,6 @@ begin end; end; - if (fOutset > 0) then - Extrude(TexBuffer, fOutset); - // allocate resources for textures and display lists glGenTextures(1, @fTexture); @@ -2151,13 +2234,14 @@ begin FT_Done_Glyph(Glyph); end; -constructor TFTGlyph.Create(Font: TFTFont; ch: WideChar; Outset: single; +constructor TFTGlyph.Create(Font: TFTFont; ch: UCS4Char; Outset: single; LoadFlags: FT_Int32); begin inherited Create(); fFont := Font; fOutset := Outset; + fCharCode := ch; // get the Freetype char-index (use default UNICODE charmap) fCharIndex := FT_Get_Char_Index(Font.fFace, FT_ULONG(ch)); @@ -2336,7 +2420,7 @@ begin InsertPos := fHash.Count; end; -function TGlyphCache.AddGlyph(ch: WideChar; const Glyph: TGlyph): boolean; +function TGlyphCache.AddGlyph(ch: UCS4Char; const Glyph: TGlyph): boolean; var BaseCode: cardinal; GlyphCode: integer; @@ -2346,7 +2430,7 @@ var begin Result := false; - BaseCode := cardinal(ch) shr 8; + BaseCode := Ord(ch) shr 8; GlyphTable := FindGlyphTable(BaseCode, InsertPos); if (GlyphTable = nil) then begin @@ -2356,7 +2440,7 @@ begin end; // get glyph table offset - GlyphCode := cardinal(ch) and $FF; + GlyphCode := Ord(ch) and $FF; // insert glyph into table if not present if (GlyphTable[GlyphCode] = nil) then begin @@ -2365,19 +2449,19 @@ begin end; end; -procedure TGlyphCache.DeleteGlyph(ch: WideChar); +procedure TGlyphCache.DeleteGlyph(ch: UCS4Char); var Table: PGlyphTable; TableIndex, GlyphIndex: integer; TableEmpty: boolean; begin // find table - Table := FindGlyphTable(cardinal(ch) shr 8, TableIndex); + Table := FindGlyphTable(Ord(ch) shr 8, TableIndex); if (Table = nil) then Exit; // find glyph - GlyphIndex := cardinal(ch) and $FF; + GlyphIndex := Ord(ch) and $FF; if (Table[GlyphIndex] <> nil) then begin // destroy glyph @@ -2402,19 +2486,19 @@ begin end; end; -function TGlyphCache.GetGlyph(ch: WideChar): TGlyph; +function TGlyphCache.GetGlyph(ch: UCS4Char): TGlyph; var InsertPos: integer; Table: PGlyphTable; begin - Table := FindGlyphTable(cardinal(ch) shr 8, InsertPos); + Table := FindGlyphTable(Ord(ch) shr 8, InsertPos); if (Table = nil) then Result := nil else - Result := Table[cardinal(ch) and $FF]; + Result := Table[Ord(ch) and $FF]; end; -function TGlyphCache.HasGlyph(ch: WideChar): boolean; +function TGlyphCache.HasGlyph(ch: UCS4Char): boolean; begin Result := (GetGlyph(ch) <> nil); end; @@ -2482,7 +2566,7 @@ end; * TBitmapFont *} -constructor TBitmapFont.Create(const Filename: string; Outline: integer; +constructor TBitmapFont.Create(const Filename: IPath; Outline: integer; Baseline, Ascender, Descender: integer); begin inherited Create(); @@ -2494,7 +2578,7 @@ begin fAscender := Ascender; fDescender := Descender; - LoadFontInfo(ChangeFileExt(Filename, '.dat')); + LoadFontInfo(Filename.SetExtension('.dat')); ResetIntern(); end; @@ -2524,27 +2608,27 @@ begin fWidths[Count] := Round(fWidths[Count] * WidthMult) + WidthAdd; end; -procedure TBitmapFont.LoadFontInfo(const InfoFile: string); +procedure TBitmapFont.LoadFontInfo(const InfoFile: IPath); var - Stream: TFileStream; + Stream: TStream; begin FillChar(fWidths[0], Length(fWidths), 0); Stream := nil; try - Stream := TFileStream.Create(InfoFile, fmOpenRead); + Stream := TBinaryFileStream.Create(InfoFile, fmOpenRead); Stream.Read(fWidths, 256); except - raise Exception.Create('Could not read font info file ''' + InfoFile + ''''); + raise Exception.Create('Could not read font info file ''' + InfoFile.ToNative + ''''); end; Stream.Free; end; -function TBitmapFont.BBox(const Text: TWideStringArray; Advance: boolean): TBoundsDbl; +function TBitmapFont.BBox(const Text: TUCS4StringArray; Advance: boolean): TBoundsDbl; var LineIndex, CharIndex: integer; CharCode: cardinal; - Line: WideString; + Line: UCS4String; LineWidth: double; begin Result.Left := 0; @@ -2556,7 +2640,7 @@ begin begin Line := Text[LineIndex]; LineWidth := 0; - for CharIndex := 1 to Length(Line) do + for CharIndex := 0 to LengthUCS4(Line)-1 do begin CharCode := Ord(Line[CharIndex]); if (CharCode < Length(fWidths)) then @@ -2567,7 +2651,7 @@ begin end; end; -procedure TBitmapFont.RenderChar(ch: WideChar; var AdvanceX: real); +procedure TBitmapFont.RenderChar(ch: UCS4Char; var AdvanceX: real); var TexX, TexY: real; TexR, TexB: real; @@ -2659,20 +2743,20 @@ begin AdvanceX := AdvanceX + GlyphWidth; end; -procedure TBitmapFont.Render(const Text: WideString); +procedure TBitmapFont.Render(const Text: UCS4String); var CharIndex: integer; AdvanceX: real; begin // if there is no text do nothing - if (Text = '') then + if (Text = nil) or (Text[0] = 0) then Exit; //Save the current color and alpha (for reflection) glGetFloatv(GL_CURRENT_COLOR, @fTempColor); AdvanceX := 0; - for CharIndex := 1 to Length(Text) do + for CharIndex := 0 to LengthUCS4(Text)-1 do begin RenderChar(Text[CharIndex], AdvanceX); end; diff --git a/Lua/src/base/UGraphic.pas b/Lua/src/base/UGraphic.pas index 99478b70..a1f63366 100644 --- a/Lua/src/base/UGraphic.pas +++ b/Lua/src/base/UGraphic.pas @@ -150,11 +150,12 @@ var //popup mod ScreenPopupCheck: TScreenPopupCheck; ScreenPopupError: TScreenPopupError; + ScreenPopupInfo: TScreenPopupInfo; //Notes - Tex_Left: array[0..6] of TTexture; //rename to tex_note_left - Tex_Mid: array[0..6] of TTexture; //rename to tex_note_mid - Tex_Right: array[0..6] of TTexture; //rename to tex_note_right + Tex_Left: array[1..6] of TTexture; //rename to tex_note_left + Tex_Mid: array[1..6] of TTexture; //rename to tex_note_mid + Tex_Right: array[1..6] of TTexture; //rename to tex_note_right Tex_plain_Left: array[1..6] of TTexture; //rename to tex_notebg_left Tex_plain_Mid: array[1..6] of TTexture; //rename to tex_notebg_mid @@ -198,7 +199,14 @@ var Tex_Score_NoteBarRound_Lightest : array [1..6] of TTexture; Tex_Score_Ratings : array [0..7] of TTexture; - + + // arrows for SelectSlide + Tex_SelectS_ArrowL: TTexture; + Tex_SelectS_ArrowR: TTexture; + + // textures for software mouse cursor + Tex_Cursor_Unpressed: TTexture; + Tex_Cursor_Pressed: TTexture; const Skin_BGColorR = 1; Skin_BGColorG = 1; @@ -232,17 +240,6 @@ const Skin_OscG = 0; Skin_OscB = 0; - // TODO: add to theme ini file - Skin_LyricsT = 493; - Skin_LyricsUpperX = 80; - Skin_LyricsUpperW = 640; - Skin_LyricsUpperY = Skin_LyricsT; - Skin_LyricsUpperH = 41; - Skin_LyricsLowerX = 80; - Skin_LyricsLowerW = 640; - Skin_LyricsLowerY = Skin_LyricsT + Skin_LyricsUpperH + 1; - Skin_LyricsLowerH = 41; - Skin_SpectrumT = 470; Skin_SpectrumBot = 570; Skin_SpectrumH = 100; @@ -280,11 +277,12 @@ function LoadingThreadFunction: integer; implementation uses + Classes, UMain, UIni, UDisplay, UCommandLine, - Classes; + UPathUtils; procedure LoadFontTextures; begin @@ -294,23 +292,13 @@ end; procedure LoadTextures; - var - P: integer; - R, G, B: real; - Col: integer; + P: integer; + R, G, B: real; + Col: integer; begin Log.LogStatus('Loading Textures', 'LoadTextures'); - // FIXME: do we need this? (REMOVE otherwise) - Tex_Left[0] := Texture.LoadTexture(Skin.GetTextureFileName('GrayLeft'), TEXTURE_TYPE_TRANSPARENT, 0); - // FIXME: do we need this? (REMOVE otherwise) - Tex_Mid[0] := Texture.LoadTexture(Skin.GetTextureFileName('GrayMid'), TEXTURE_TYPE_PLAIN, 0); - // FIXME: do we need this? (REMOVE otherwise) - Tex_Right[0] := Texture.LoadTexture(Skin.GetTextureFileName('GrayRight'), TEXTURE_TYPE_TRANSPARENT, 0); - - Log.LogStatus('Loading Textures - A', 'LoadTextures'); - // P1-6 // TODO... do it once for each player... this is a bit crappy !! // can we make it any better !? @@ -318,7 +306,29 @@ begin begin LoadColor(R, G, B, 'P' + IntToStr(P) + 'Light'); Col := $10000 * Round(R*255) + $100 * Round(G*255) + Round(B*255); - + + { some colors for tests + Col := $10000 * Round(0.02*255) + $100 * Round(0.6 *255) + Round(0.8 *255); //blue + Col := $10000 * Round(0.8 *255) ; //red + Col := $100 * Round(0.85*255) ; //green + Col := $10000 * 255 + $100 * Round(0.52*255) ; //orange + Col := $10000 * 255 + $100 * 255 ; //yellow + Col := $10000 * Round(0.82*255) + 255 ; //purple + Col := $10000 * Round(0.22*255) + $100 * Round(0.39*255) + Round(0.64*255); //dark blue + Col := $10000 * Round(0 *255) + $100 * Round(0 *255) + Round(0 *255); //black + Col := $10000 * Round(1.0 *255) + $100 * Round(0.43*255) + Round(0.70*255); //pink + Col := 0; //black + Col := $FFFFFF; //white + Col := $FF0000; //red + Col := $00FF00; //green + Col := $002200; //light green + Col := $002222; //light greenblue + Col := $222200; //light yellow + Col := $340000; //red + Col := $FF6EB4; //pink + Col := $333333; //grey + } + Tex_Left[P] := Texture.LoadTexture(Skin.GetTextureFileName('GrayLeft'), TEXTURE_TYPE_COLORIZED, Col); Tex_Mid[P] := Texture.LoadTexture(Skin.GetTextureFileName('GrayMid'), TEXTURE_TYPE_COLORIZED, Col); Tex_Right[P] := Texture.LoadTexture(Skin.GetTextureFileName('GrayRight'), TEXTURE_TYPE_COLORIZED, Col); @@ -339,6 +349,15 @@ begin Tex_Ball := Texture.LoadTexture(Skin.GetTextureFileName('Ball'), TEXTURE_TYPE_TRANSPARENT, $FF00FF); Tex_Lyric_Help_Bar := Texture.LoadTexture(Skin.GetTextureFileName('LyricHelpBar'), TEXTURE_TYPE_TRANSPARENT, $FF00FF); + Tex_SelectS_ArrowL := Texture.LoadTexture(Skin.GetTextureFileName('Select_ArrowLeft'), TEXTURE_TYPE_TRANSPARENT, 0); + Tex_SelectS_ArrowR := Texture.LoadTexture(Skin.GetTextureFileName('Select_ArrowRight'), TEXTURE_TYPE_TRANSPARENT, 0); + + Tex_Cursor_Unpressed := Texture.LoadTexture(Skin.GetTextureFileName('Cursor'), TEXTURE_TYPE_TRANSPARENT, 0); + + if (Skin.GetTextureFileName('Cursor_Pressed').IsSet) then + Tex_Cursor_Pressed := Texture.LoadTexture(Skin.GetTextureFileName('Cursor_Pressed'), TEXTURE_TYPE_TRANSPARENT, 0) + else + Tex_Cursor_Pressed.TexNum := 0; //TimeBar mod Tex_TimeProgress := Texture.LoadTexture(Skin.GetTextureFileName('TimeBar')); @@ -384,14 +403,14 @@ begin End; Col := $10000 * Round(R*255) + $100 * Round(G*255) + Round(B*255); - Tex_SingLineBonusBack[P] := Texture.LoadTexture(pchar(Skin.GetTextureFileName('LineBonusBack')), TEXTURE_TYPE_COLORIZED, Col); + Tex_SingLineBonusBack[P] := Texture.LoadTexture(Skin.GetTextureFileName('LineBonusBack'), TEXTURE_TYPE_COLORIZED, Col); end; //## backgrounds for the scores ## for P := 0 to 5 do begin LoadColor(R, G, B, 'P' + IntToStr(P+1) + 'Light'); Col := $10000 * Round(R*255) + $100 * Round(G*255) + Round(B*255); - Tex_ScoreBG[P] := Texture.LoadTexture(pchar(Skin.GetTextureFileName('ScoreBG')), TEXTURE_TYPE_COLORIZED, Col); + Tex_ScoreBG[P] := Texture.LoadTexture(Skin.GetTextureFileName('ScoreBG'), TEXTURE_TYPE_COLORIZED, Col); end; @@ -406,23 +425,23 @@ begin //NoteBar ScoreBar LoadColor(R, G, B, 'P' + IntToStr(P) + 'Dark'); Col := $10000 * Round(R*255) + $100 * Round(G*255) + Round(B*255); - Tex_Score_NoteBarLevel_Dark[P] := Texture.LoadTexture(pchar(Skin.GetTextureFileName('ScoreLevel_Dark')), TEXTURE_TYPE_COLORIZED, Col); - Tex_Score_NoteBarRound_Dark[P] := Texture.LoadTexture(pchar(Skin.GetTextureFileName('ScoreLevel_Dark_Round')), TEXTURE_TYPE_COLORIZED, Col); + Tex_Score_NoteBarLevel_Dark[P] := Texture.LoadTexture(Skin.GetTextureFileName('ScoreLevel_Dark'), TEXTURE_TYPE_COLORIZED, Col); + Tex_Score_NoteBarRound_Dark[P] := Texture.LoadTexture(Skin.GetTextureFileName('ScoreLevel_Dark_Round'), TEXTURE_TYPE_COLORIZED, Col); //LineBonus ScoreBar LoadColor(R, G, B, 'P' + IntToStr(P) + 'Light'); Col := $10000 * Round(R*255) + $100 * Round(G*255) + Round(B*255); - Tex_Score_NoteBarLevel_Light[P] := Texture.LoadTexture(pchar(Skin.GetTextureFileName('ScoreLevel_Light')), TEXTURE_TYPE_COLORIZED, Col); - Tex_Score_NoteBarRound_Light[P] := Texture.LoadTexture(pchar(Skin.GetTextureFileName('ScoreLevel_Light_Round')), TEXTURE_TYPE_COLORIZED, Col); + Tex_Score_NoteBarLevel_Light[P] := Texture.LoadTexture(Skin.GetTextureFileName('ScoreLevel_Light'), TEXTURE_TYPE_COLORIZED, Col); + Tex_Score_NoteBarRound_Light[P] := Texture.LoadTexture(Skin.GetTextureFileName('ScoreLevel_Light_Round'), TEXTURE_TYPE_COLORIZED, Col); //GoldenNotes ScoreBar LoadColor(R, G, B, 'P' + IntToStr(P) + 'Lightest'); Col := $10000 * Round(R*255) + $100 * Round(G*255) + Round(B*255); - Tex_Score_NoteBarLevel_Lightest[P] := Texture.LoadTexture(pchar(Skin.GetTextureFileName('ScoreLevel_Lightest')), TEXTURE_TYPE_COLORIZED, Col); - Tex_Score_NoteBarRound_Lightest[P] := Texture.LoadTexture(pchar(Skin.GetTextureFileName('ScoreLevel_Lightest_Round')), TEXTURE_TYPE_COLORIZED, Col); + Tex_Score_NoteBarLevel_Lightest[P] := Texture.LoadTexture(Skin.GetTextureFileName('ScoreLevel_Lightest'), TEXTURE_TYPE_COLORIZED, Col); + Tex_Score_NoteBarRound_Lightest[P] := Texture.LoadTexture(Skin.GetTextureFileName('ScoreLevel_Lightest_Round'), TEXTURE_TYPE_COLORIZED, Col); end; //## rating pictures that show a picture according to your rate ## for P := 0 to 7 do begin - Tex_Score_Ratings[P] := Texture.LoadTexture(pchar(Skin.GetTextureFileName('Rating_'+IntToStr(P))), TEXTURE_TYPE_TRANSPARENT, 0); + Tex_Score_Ratings[P] := Texture.LoadTexture(Skin.GetTextureFileName('Rating_'+IntToStr(P)), TEXTURE_TYPE_TRANSPARENT, 0); end; Log.LogStatus('Loading Textures - Done', 'LoadTextures'); @@ -459,9 +478,9 @@ begin end; // load icon image (must be 32x32 for win32) - Icon := LoadImage(ResourcesPath + WINDOW_ICON); + Icon := LoadImage(ResourcesPath.Append(WINDOW_ICON)); if (Icon <> nil) then - SDL_WM_SetIcon(Icon, 0); + SDL_WM_SetIcon(Icon, nil); SDL_WM_SetCaption(PChar(Title), nil); @@ -497,6 +516,7 @@ begin Log.LogStatus('TDisplay.Create', 'UGraphic.Initialize3D'); Display := TDisplay.Create; + //Display.SetCursor; //Log.BenchmarkEnd(2); Log.LogBenchmark('====> Creating Display', 2); @@ -629,15 +649,15 @@ begin begin Log.LogStatus('SDL_SetVideoMode', 'Set Video Mode... Full Screen'); screen := SDL_SetVideoMode(W, H, (Depth+1) * 16, SDL_OPENGL or SDL_FULLSCREEN ); - SDL_ShowCursor(0); end else begin Log.LogStatus('SDL_SetVideoMode', 'Set Video Mode... Windowed'); screen := SDL_SetVideoMode(W, H, 0, SDL_OPENGL or SDL_RESIZABLE); - SDL_ShowCursor(1); end; + SDL_ShowCursor(0); + if (screen = nil) then begin Log.LogCritical('SDL_SetVideoMode Failed', 'Initialize3D'); @@ -661,7 +681,7 @@ end; procedure LoadLoadingScreen; begin ScreenLoading := TScreenLoading.Create; - ScreenLoading.onShow; + ScreenLoading.OnShow; Display.CurrentScreen := @ScreenLoading; @@ -676,7 +696,7 @@ end; procedure LoadScreens; begin { ScreenLoading := TScreenLoading.Create; - ScreenLoading.onShow; + ScreenLoading.OnShow; Display.CurrentScreen := @ScreenLoading; ScreenLoading.Draw; Display.Draw; @@ -737,6 +757,8 @@ begin Log.BenchmarkEnd(3); Log.LogBenchmark('====> Screen Popup (Check)', 3); Log.BenchmarkStart(3); ScreenPopupError := TScreenPopupError.Create; Log.BenchmarkEnd(3); Log.LogBenchmark('====> Screen Popup (Error)', 3); Log.BenchmarkStart(3); + ScreenPopupInfo := TScreenPopupInfo.Create; + Log.BenchmarkEnd(3); Log.LogBenchmark('====> Screen Popup (Info)', 3); Log.BenchmarkStart(3); ScreenPartyNewRound := TScreenPartyNewRound.Create; Log.BenchmarkEnd(3); Log.LogBenchmark('====> Screen PartyNewRound', 3); Log.BenchmarkStart(3); ScreenPartyScore := TScreenPartyScore.Create; @@ -788,6 +810,7 @@ begin ScreenSongJumpto.Destroy; ScreenPopupCheck.Destroy; ScreenPopupError.Destroy; + ScreenPopupInfo.Destroy; ScreenPartyNewRound.Destroy; ScreenPartyScore.Destroy; ScreenPartyWin.Destroy; diff --git a/Lua/src/base/UGraphicClasses.pas b/Lua/src/base/UGraphicClasses.pas index 3fbe262f..cdaa238e 100644 --- a/Lua/src/base/UGraphicClasses.pas +++ b/Lua/src/base/UGraphicClasses.pas @@ -124,16 +124,16 @@ var implementation uses - sysutils, + SysUtils, + Math, gl, + UCommon, + UDrawTexture, + UGraphic, UIni, - UMain, - UThemes, + UNote, USkins, - UGraphic, - UDrawTexture, - UCommon, - math; + UThemes; //TParticle constructor TParticle.Create(cX, cY : real; diff --git a/Lua/src/base/UHooks.pas b/Lua/src/base/UHooks.pas deleted file mode 100644 index ab830090..00000000 --- a/Lua/src/base/UHooks.pas +++ /dev/null @@ -1,461 +0,0 @@ -{* 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 UHooks; - -{********************* - THookManager - Class for saving, managing and calling of hooks. - Saves all hookable events and their subscribers -*********************} -interface - -{$IFDEF FPC} - {$MODE Delphi} -{$ENDIF} - -{$I switches.inc} - -uses - uPluginDefs, - SysUtils; - -type - //Record that saves info from Subscriber - PSubscriberInfo = ^TSubscriberInfo; - TSubscriberInfo = record - Self: THandle; //ID of this Subscription (First word: ID of Subscription; 2nd word: ID of Hook) - Next: PSubscriberInfo; //Pointer to next Item in HookChain - - Owner: integer; //For Error Handling and Plugin Unloading. - - //Here is s/t tricky - //To avoid writing of Wrapping Functions to Hook an Event with a Class - //We save a Normal Proc or a Method of a Class - case isClass: boolean of - False: (Proc: TUS_Hook); //Proc that will be called on Event - True: (ProcOfClass: TUS_Hook_of_Object); - end; - - TEventInfo = record - Name: string[60]; //Name of Event - FirstSubscriber: PSubscriberInfo; //First subscriber in chain - LastSubscriber: PSubscriberInfo; //Last " (for easier subscriber adding - end; - - THookManager = class - private - Events: array of TEventInfo; - SpaceinEvents: word; //Number of empty Items in Events Array. (e.g. Deleted Items) - - procedure FreeSubscriber(const EventIndex: word; const Last, Cur: PSubscriberInfo); - public - constructor Create(const SpacetoAllocate: word); - - function AddEvent (const EventName: Pchar): THandle; - function DelEvent (hEvent: THandle): integer; - - function AddSubscriber (const EventName: Pchar; const Proc: TUS_Hook = nil; const ProcOfClass: TUS_Hook_of_Object = nil): THandle; - function DelSubscriber (const hSubscriber: THandle): integer; - - function CallEventChain (const hEvent: THandle; const wParam: TwParam; lParam: TlParam): integer; - function EventExists (const EventName: Pchar): integer; - - procedure DelbyOwner(const Owner: integer); - end; - -function HookTest(wParam: TwParam; lParam: TlParam): integer; stdcall; - -var - HookManager: THookManager; - -implementation - -uses - ULog, - UCore; - -//------------ -// Create - Creates Class and Set Standard Values -//------------ -constructor THookManager.Create(const SpacetoAllocate: word); -var I: integer; -begin - inherited Create(); - - //Get the Space and "Zero" it - SetLength (Events, SpacetoAllocate); - for I := 0 to SpacetoAllocate-1 do - Events[I].Name[1] := chr(0); - - SpaceinEvents := SpacetoAllocate; - - {$IFDEF DEBUG} - debugWriteLn('HookManager: Succesful Created.'); - {$ENDIF} -end; - -//------------ -// AddEvent - Adds an Event and return the Events Handle or 0 on Failure -//------------ -function THookManager.AddEvent (const EventName: Pchar): THandle; -var I: integer; -begin - Result := 0; - - if (EventExists(EventName) = 0) then - begin - if (SpaceinEvents > 0) then - begin - //There is already Space available - //Go Search it! - for I := 0 to High(Events) do - if (Events[I].Name[1] = chr(0)) then - begin //Found Space - Result := I; - Dec(SpaceinEvents); - Break; - end; - - {$IFDEF DEBUG} - debugWriteLn('HookManager: Found Space for Event at Handle: ''' + InttoStr(Result+1) + ''); - {$ENDIF} - end - else - begin //There is no Space => Go make some! - Result := Length(Events); - SetLength(Events, Result + 1); - end; - - //Set Events Data - Events[Result].Name := EventName; - Events[Result].FirstSubscriber := nil; - Events[Result].LastSubscriber := nil; - - //Handle is Index + 1 - Inc(Result); - - {$IFDEF DEBUG} - debugWriteLn('HookManager: Add Event succesful: ''' + EventName + ''); - {$ENDIF} - end - {$IFDEF DEBUG} - else - debugWriteLn('HookManager: Trying to ReAdd Event: ''' + EventName + ''); - {$ENDIF} -end; - -//------------ -// DelEvent - Deletes an Event by Handle Returns False on Failure -//------------ -function THookManager.DelEvent (hEvent: THandle): integer; -var - Cur, Last: PSubscriberInfo; -begin - hEvent := hEvent - 1; //Arrayindex is Handle - 1 - Result := -1; - - - if (Length(Events) > hEvent) and (Events[hEvent].Name[1] <> chr(0)) then - begin //Event exists - //Free the Space for all Subscribers - Cur := Events[hEvent].FirstSubscriber; - - while (Cur <> nil) do - begin - Last := Cur; - Cur := Cur.Next; - FreeMem(Last, SizeOf(TSubscriberInfo)); - end; - - {$IFDEF DEBUG} - debugWriteLn('HookManager: Removed Event succesful: ''' + Events[hEvent].Name + ''); - {$ENDIF} - - //Free the Event - Events[hEvent].Name[1] := chr(0); - Inc(SpaceinEvents); //There is one more space for new events - end - - {$IFDEF DEBUG} - else - debugWriteLn('HookManager: Try to Remove not Existing Event. Handle: ''' + InttoStr(hEvent) + ''); - {$ENDIF} -end; - -//------------ -// AddSubscriber - Adds an Subscriber to the Event by Name -// Returns Handle of the Subscribtion or 0 on Failure -//------------ -function THookManager.AddSubscriber (const EventName: Pchar; const Proc: TUS_Hook; const ProcOfClass: TUS_Hook_of_Object): THandle; -var - EventHandle: THandle; - EventIndex: Cardinal; - Cur: PSubscriberInfo; -begin - Result := 0; - - if (@Proc <> nil) or (@ProcOfClass <> nil) then - begin - EventHandle := EventExists(EventName); - - if (EventHandle <> 0) then - begin - EventIndex := EventHandle - 1; - - //Get Memory - GetMem(Cur, SizeOf(TSubscriberInfo)); - - //Fill it with Data - Cur.Next := nil; - - //Add Owner - Cur.Owner := Core.CurExecuted; - - if (@Proc = nil) then - begin //Use the ProcofClass Method - Cur.isClass := True; - Cur.ProcOfClass := ProcofClass; - end - else //Use the normal Proc - begin - Cur.isClass := False; - Cur.Proc := Proc; - end; - - //Create Handle (1st word: Handle of Event; 2nd word: unique ID - if (Events[EventIndex].LastSubscriber = nil) then - begin - if (Events[EventIndex].FirstSubscriber = nil) then - begin - Result := (EventHandle SHL 16); - Events[EventIndex].FirstSubscriber := Cur; - end - else - begin - Result := Events[EventIndex].FirstSubscriber.Self + 1; - end; - end - else - begin - Result := Events[EventIndex].LastSubscriber.Self + 1; - Events[EventIndex].LastSubscriber.Next := Cur; - end; - - Cur.Self := Result; - - //Add to Chain - Events[EventIndex].LastSubscriber := Cur; - - {$IFDEF DEBUG} - debugWriteLn('HookManager: Add Subscriber to Event ''' + Events[EventIndex].Name + ''' succesful. Handle: ''' + InttoStr(Result) + ''' Owner: ' + InttoStr(Cur.Owner)); - {$ENDIF} - end; - end; -end; - -//------------ -// FreeSubscriber - Helper for DelSubscriber. Prevents Loss of Chain Items. Frees Memory. -//------------ -procedure THookManager.FreeSubscriber(const EventIndex: word; const Last, Cur: PSubscriberInfo); -begin - //Delete from Chain - if (Last <> nil) then - begin - Last.Next := Cur.Next; - end - else //Was first Popup - begin - Events[EventIndex].FirstSubscriber := Cur.Next; - end; - - //Was this Last subscription ? - if (Cur = Events[EventIndex].LastSubscriber) then - begin //Change Last Subscriber - Events[EventIndex].LastSubscriber := Last; - end; - - //Free Space: - FreeMem(Cur, SizeOf(TSubscriberInfo)); -end; - -//------------ -// DelSubscriber - Deletes a Subscribtion by Handle, return non Zero on Failure -//------------ -function THookManager.DelSubscriber (const hSubscriber: THandle): integer; -var - EventIndex: Cardinal; - Cur, Last: PSubscriberInfo; -begin - Result := -1; - EventIndex := ((hSubscriber and (High(THandle) xor High(word))) SHR 16) - 1; - - //Existing Event ? - if (EventIndex < Length(Events)) and (Events[EventIndex].Name[1] <> chr(0)) then - begin - Result := -2; //Return -1 on not existing Event, -2 on not existing Subscription - - //Search for Subscription - Cur := Events[EventIndex].FirstSubscriber; - Last := nil; - - //go through the chain ... - while (Cur <> nil) do - begin - if (Cur.Self = hSubscriber) then - begin //Found Subscription we searched for - FreeSubscriber(EventIndex, Last, Cur); - - {$IFDEF DEBUG} - debugWriteLn('HookManager: Del Subscriber from Event ''' + Events[EventIndex].Name + ''' succesful. Handle: ''' + InttoStr(hSubscriber) + ''); - {$ENDIF} - - //Set Result and Break the Loop - Result := 0; - Break; - end; - - Last := Cur; - Cur := Cur.Next; - end; - - end; -end; - - -//------------ -// CallEventChain - Calls the Chain of a specified EventHandle -// Returns: -1: Handle doesn't Exist, 0 Chain is called until the End -//------------ -function THookManager.CallEventChain (const hEvent: THandle; const wParam: TwParam; lParam: TlParam): integer; -var - EventIndex: Cardinal; - Cur: PSubscriberInfo; - CurExecutedBackup: integer; //backup of Core.CurExecuted Attribute -begin - Result := -1; - EventIndex := hEvent - 1; - - if ((EventIndex <= High(Events)) and (Events[EventIndex].Name[1] <> chr(0))) then - begin //Existing Event - //Backup CurExecuted - CurExecutedBackup := Core.CurExecuted; - - //Start calling the Chain !!!11 - Cur := Events[EventIndex].FirstSubscriber; - Result := 0; - //Call Hooks until the Chain is at the End or breaked - while ((Cur <> nil) and (Result = 0)) do - begin - //Set CurExecuted - Core.CurExecuted := Cur.Owner; - if (Cur.isClass) then - Result := Cur.ProcOfClass(wParam, lParam) - else - Result := Cur.Proc(wParam, lParam); - - Cur := Cur.Next; - end; - - //Restore CurExecuted - Core.CurExecuted := CurExecutedBackup; - end; - - {$IFDEF DEBUG} - debugWriteLn('HookManager: Called Chain from Event ''' + Events[EventIndex].Name + ''' succesful. Result: ''' + InttoStr(Result) + ''); - {$ENDIF} -end; - -//------------ -// EventExists - Returns non Zero if an Event with the given Name exists -//------------ -function THookManager.EventExists (const EventName: Pchar): integer; -var - I: integer; - Name: string[60]; -begin - Result := 0; - //if (Length(EventName) < - Name := string(EventName); - - //Sure not to search for empty space - if (Name[1] <> chr(0)) then - begin - //Search for Event - for I := 0 to High(Events) do - if (Events[I].Name = Name) then - begin //Event found - Result := I + 1; - Break; - end; - end; -end; - -//------------ -// DelbyOwner - Dels all Subscriptions by a specific Owner. (For Clean Plugin/Module unloading) -//------------ -procedure THookManager.DelbyOwner(const Owner: integer); -var - I: integer; - Cur, Last: PSubscriberInfo; -begin - //Search for Owner in all Hooks Chains - for I := 0 to High(Events) do - begin - if (Events[I].Name[1] <> chr(0)) then - begin - - Last := nil; - Cur := Events[I].FirstSubscriber; - //Went Through Chain - while (Cur <> nil) do - begin - if (Cur.Owner = Owner) then - begin //Found Subscription by Owner -> Delete - FreeSubscriber(I, Last, Cur); - if (Last <> nil) then - Cur := Last.Next - else - Cur := Events[I].FirstSubscriber; - end - else - begin - //Next Item: - Last := Cur; - Cur := Cur.Next; - end; - end; - end; - end; -end; - - -function HookTest(wParam: TwParam; lParam: TlParam): integer; stdcall; -begin - Result := 0; //Don't break the chain - Core.ShowMessage(CORE_SM_INFO, Pchar(string(Pchar(Pointer(lParam))) + ': ' + string(Pchar(Pointer(wParam))))); -end; - -end. diff --git a/Lua/src/base/UImage.pas b/Lua/src/base/UImage.pas index 18b0035c..1866316e 100644 --- a/Lua/src/base/UImage.pas +++ b/Lua/src/base/UImage.pas @@ -34,7 +34,8 @@ interface {$I switches.inc} uses - SDL; + SDL, + UPath; {$DEFINE HavePNG} {$DEFINE HaveBMP} @@ -131,30 +132,29 @@ type *******************************************************) {$IFDEF HavePNG} -function WritePNGImage(const FileName: string; Surface: PSDL_Surface): boolean; +function WritePNGImage(const FileName: IPath; Surface: PSDL_Surface): boolean; {$ENDIF} {$IFDEF HaveBMP} -function WriteBMPImage(const FileName: string; Surface: PSDL_Surface): boolean; +function WriteBMPImage(const FileName: IPath; Surface: PSDL_Surface): boolean; {$ENDIF} {$IFDEF HaveJPG} -function WriteJPGImage(const FileName: string; Surface: PSDL_Surface; Quality: integer): boolean; +function WriteJPGImage(const FileName: IPath; Surface: PSDL_Surface; Quality: integer): boolean; {$ENDIF} (******************************************************* * Image loading *******************************************************) -function LoadImage(const Filename: string): PSDL_Surface; +function LoadImage(const Filename: IPath): PSDL_Surface; (******************************************************* * Image manipulation *******************************************************) function PixelFormatEquals(fmt1, fmt2: PSDL_PixelFormat): boolean; -procedure ScaleImage(var ImgSurface: PSDL_Surface; Width, Height: Cardinal); -procedure FitImage(var ImgSurface: PSDL_Surface; Width, Height: Cardinal); -procedure ColorizeImage(ImgSurface: PSDL_Surface; NewColor: Cardinal); - +procedure ScaleImage(var ImgSurface: PSDL_Surface; Width, Height: cardinal); +procedure FitImage(var ImgSurface: PSDL_Surface; Width, Height: cardinal); +procedure ColorizeImage(ImgSurface: PSDL_Surface; NewColor: cardinal); implementation @@ -182,10 +182,10 @@ uses zlib, sdl_image, sdlutils, + sdlstreams, UCommon, ULog; - function IsRGBSurface(pixelFmt: PSDL_PixelFormat): boolean; begin Result := (pixelFmt.BitsPerPixel = 24) and @@ -266,7 +266,6 @@ begin end; end; - (******************************************************* * Image saving *******************************************************) @@ -285,26 +284,26 @@ end; procedure user_read_data(png_ptr: png_structp; data: png_bytep; length: png_size_t); cdecl; var - inFile: TFileStream; + inFile: TStream; begin - inFile := TFileStream(png_get_io_ptr(png_ptr)); + inFile := TStream(png_get_io_ptr(png_ptr)); inFile.Read(data^, length); end; procedure user_write_data(png_ptr: png_structp; data: png_bytep; length: png_size_t); cdecl; var - outFile: TFileStream; + outFile: TStream; begin - outFile := TFileStream(png_get_io_ptr(png_ptr)); + outFile := TStream(png_get_io_ptr(png_ptr)); outFile.Write(data^, length); end; procedure user_flush_data(png_ptr: png_structp); cdecl; //var -// outFile: TFileStream; +// outFile: TStream; begin // binary files are flushed automatically, Flush() works with Text-files only - //outFile := TFileStream(png_get_io_ptr(png_ptr)); + //outFile := TStream(png_get_io_ptr(png_ptr)); //outFile.Flush(); end; @@ -314,23 +313,23 @@ var hour, minute, second, msecond: word; begin DecodeDate(time, year, month, day); - pngTime.year := year; - pngTime.month := month; - pngTime.day := day; + pngTime.year := png_uint_16(year); + pngTime.month := png_byte(month); + pngTime.day := png_byte(day); DecodeTime(time, hour, minute, second, msecond); - pngTime.hour := hour; - pngTime.minute := minute; - pngTime.second := second; + pngTime.hour := png_byte(hour); + pngTime.minute := png_byte(minute); + pngTime.second := png_byte(second); end; (* * ImageData must be in RGB-format *) -function WritePNGImage(const FileName: string; Surface: PSDL_Surface): boolean; +function WritePNGImage(const FileName: IPath; Surface: PSDL_Surface): boolean; var png_ptr: png_structp; info_ptr: png_infop; - pngFile: TFileStream; + pngFile: TStream; row: integer; rowData: array of png_bytep; // rowStride: integer; @@ -342,9 +341,9 @@ begin // open file for writing try - pngFile := TFileStream.Create(FileName, fmCreate); + pngFile := TBinaryFileStream.Create(FileName, fmCreate); except - Log.LogError('Could not open file: "' + FileName + '"', 'WritePngImage'); + Log.LogError('Could not open file: "' + FileName.ToNative + '"', 'WritePngImage'); Exit; end; @@ -503,9 +502,9 @@ type (* * ImageData must be in BGR-format *) -function WriteBMPImage(const FileName: string; Surface: PSDL_Surface): boolean; +function WriteBMPImage(const FileName: IPath; Surface: PSDL_Surface): boolean; var - bmpFile: TFileStream; + bmpFile: TStream; FileInfo: BITMAPINFOHEADER; FileHeader: BITMAPFILEHEADER; Converted: boolean; @@ -516,9 +515,9 @@ begin // open file for writing try - bmpFile := TFileStream.Create(FileName, fmCreate); + bmpFile := TBinaryFileStream.Create(FileName, fmCreate); except - Log.LogError('Could not open file: "' + FileName + '"', 'WriteBMPImage'); + Log.LogError('Could not open file: "' + FileName.ToNative + '"', 'WriteBMPImage'); Exit; end; @@ -582,7 +581,7 @@ begin Result := true; finally - Log.LogError('Could not write file: "' + FileName + '"', 'WriteBMPImage'); + Log.LogError('Could not write file: "' + FileName.ToNative + '"', 'WriteBMPImage'); end; if (Converted) then @@ -600,20 +599,21 @@ end; {$IFDEF HaveJPG} -function WriteJPGImage(const FileName: string; Surface: PSDL_Surface; Quality: integer): boolean; +function WriteJPGImage(const FileName: IPath; Surface: PSDL_Surface; Quality: integer): boolean; var {$IFDEF Delphi} - Bitmap: TBitmap; + Bitmap: TBitmap; BitmapInfo: TBitmapInfo; - Jpeg: TJpegImage; - row: integer; + Jpeg: TJpegImage; + row: integer; + FileStream: TBinaryFileStream; {$ELSE} cinfo: jpeg_compress_struct; jerr : jpeg_error_mgr; - jpgFile: TFileStream; + jpgFile: TBinaryFileStream; rowPtr: array[0..0] of JSAMPROW; {$ENDIF} - converted: boolean; + converted: boolean; begin Result := false; @@ -672,19 +672,32 @@ begin SDL_UnlockSurface(Surface); // assign Bitmap to JPEG and store the latter - Jpeg := TJPEGImage.Create; - Jpeg.Assign(Bitmap); - Bitmap.Free; - Jpeg.CompressionQuality := Quality; try - // compress image (don't forget this line, otherwise it won't be compressed) - Jpeg.Compress(); - Jpeg.SaveToFile(FileName); + // init with nil so Free() will not fail if an exception occurs + Jpeg := nil; + Bitmap := nil; + FileStream := nil; + + try + Jpeg := TJPEGImage.Create; + Jpeg.Assign(Bitmap); + + // compress image (don't forget this line, otherwise it won't be compressed) + Jpeg.CompressionQuality := Quality; + Jpeg.Compress(); + + // Note: FileStream needed for unicode filename support + FileStream := TBinaryFileStream.Create(Filename, fmCreate); + Jpeg.SaveToStream(FileStream); + finally + FileStream.Free; + Bitmap.Free; + Jpeg.Free; + end; except - Log.LogError('Could not save file: "' + FileName + '"', 'WriteJPGImage'); + Log.LogError('Could not save file: "' + FileName.ToNative + '"', 'WriteJPGImage'); Exit; end; - Jpeg.Free; {$ELSE} // based on example.pas in FPC's packages/base/pasjpeg directory @@ -706,9 +719,9 @@ begin // open file for writing try - jpgFile := TFileStream.Create(FileName, fmCreate); + jpgFile := TBinaryFileStream.Create(FileName, fmCreate); except - Log.LogError('Could not open file: "' + FileName + '"', 'WriteJPGImage'); + Log.LogError('Could not open file: "' + FileName.ToNative + '"', 'WriteJPGImage'); Exit; end; @@ -759,62 +772,56 @@ end; {$ENDIF} - (******************************************************* * Image loading *******************************************************) - (* * Loads an image from the given file *) -function LoadImage(const Filename: string): PSDL_Surface; +function LoadImage(const Filename: IPath): PSDL_Surface; var - FilenameFound: string; + FilenameCaseAdj: IPath; + FileStream: TBinaryFileStream; + SDLStream: PSDL_RWops; begin - Result := nil; - - // FileExistsInsensitive() requires a var-arg - FilenameFound := Filename; + Result := nil; - // try to find the file case insensitive - if (not FileExistsInsensitive(FilenameFound)) then + // try to adjust filename's case and check if it exists + FilenameCaseAdj := Filename.AdjustCase(false); + if (not FilenameCaseAdj.IsFile) then begin - Log.LogError('Image-File does not exist "'+FilenameFound+'"', 'LoadImage'); + Log.LogError('Image-File does not exist "' + FilenameCaseAdj.ToNative + '"', 'LoadImage'); Exit; end; // load from file try - Result := IMG_Load(PChar(FilenameFound)); + SDLStream := SDLStreamSetup(TBinaryFileStream.Create(FilenameCaseAdj, fmOpenRead)); + Result := IMG_Load_RW(SDLStream, 1); + // Note: TBinaryFileStream is freed by SDLStream. SDLStream by IMG_Load_RW(). except - Log.LogError('Could not load from file "'+FilenameFound+'"', 'LoadImage'); + Log.LogError('Could not load from file "' + FilenameCaseAdj.ToNative + '"', 'LoadImage'); Exit; end; end; - (******************************************************* * Image manipulation *******************************************************) - function PixelFormatEquals(fmt1, fmt2: PSDL_PixelFormat): boolean; begin - if (fmt1^.BitsPerPixel = fmt2^.BitsPerPixel) and - (fmt1^.BytesPerPixel = fmt2^.BytesPerPixel) and - (fmt1^.Rloss = fmt2^.Rloss) and (fmt1^.Gloss = fmt2^.Gloss) and - (fmt1^.Bloss = fmt2^.Bloss) and (fmt1^.Rmask = fmt2^.Rmask) and - (fmt1^.Gmask = fmt2^.Gmask) and (fmt1^.Bmask = fmt2^.Bmask) and - (fmt1^.Rshift = fmt2^.Rshift) and (fmt1^.Gshift = fmt2^.Gshift) and - (fmt1^.Bshift = fmt2^.Bshift) - then - Result := true - else - Result := false; + Result := + (fmt1^.BitsPerPixel = fmt2^.BitsPerPixel) and + (fmt1^.BytesPerPixel = fmt2^.BytesPerPixel) and + (fmt1^.Rloss = fmt2^.Rloss) and (fmt1^.Gloss = fmt2^.Gloss) and (fmt1^.Bloss = fmt2^.Bloss) and + (fmt1^.Rmask = fmt2^.Rmask) and (fmt1^.Gmask = fmt2^.Gmask) and (fmt1^.Bmask = fmt2^.Bmask) and + (fmt1^.Rshift = fmt2^.Rshift) and (fmt1^.Gshift = fmt2^.Gshift) and (fmt1^.Bshift = fmt2^.Bshift) + ; end; -procedure ScaleImage(var ImgSurface: PSDL_Surface; Width, Height: Cardinal); +procedure ScaleImage(var ImgSurface: PSDL_Surface; Width, Height: cardinal); var TempSurface: PSDL_Surface; begin @@ -825,10 +832,10 @@ begin SDL_FreeSurface(TempSurface); end; -procedure FitImage(var ImgSurface: PSDL_Surface; Width, Height: Cardinal); +procedure FitImage(var ImgSurface: PSDL_Surface; Width, Height: cardinal); var TempSurface: PSDL_Surface; - ImgFmt: PSDL_PixelFormat; + ImgFmt: PSDL_PixelFormat; begin TempSurface := ImgSurface; @@ -849,12 +856,12 @@ end; (* // Old slow floating point version of ColorizeTexture. // For an easier understanding of the faster fixed point version below. -procedure ColorizeTexture(TexSurface: PSDL_Surface; Col: Cardinal); +procedure ColorizeTexture(TexSurface: PSDL_Surface; Col: cardinal); var - clr: array[0..2] of Double; // [0: R, 1: G, 2: B] - hsv: array[0..2] of Double; // [0: H(ue), 1: S(aturation), 2: V(alue)] - delta, f, p, q, t: Double; - max: Double; + clr: array[0..2] of double; // [0: R, 1: G, 2: B] + hsv: array[0..2] of double; // [0: H(ue), 1: S(aturation), 2: V(alue)] + delta, f, p, q, t: double; + max: double; begin clr[0] := PixelColors[0]/255; clr[1] := PixelColors[1]/255; @@ -892,90 +899,230 @@ begin end; *) -procedure ColorizeImage(ImgSurface: PSDL_Surface; NewColor: Cardinal); +procedure ColorizeImage(ImgSurface: PSDL_Surface; NewColor: longword); - //returns hue within range [0.0-6.0) - function col2hue(Color:Cardinal): double; + // First, the rgb colors are converted to hsv, second hue is replaced by + // the NewColor, saturation and value remain unchanged, finally this + // hsv color is converted back to rgb space. + // For the conversion algorithms of colors from rgb to hsv space + // and back simply check the wikipedia. + // In order to speed up starting time of USDX the division of reals is + // replaced by division of longints, shifted by 10 bits to keep + // digits. + + // The use of longwards leeds to some type size mismatch warnings + // whenever differences are formed. + // This should not be a problem, since the results should all be positive. + // replacing longword by longint would probably resolve this cosmetic fault :-) + + function ColorToHue(const Color: longword): longword; + // returns hue within the range [0.0-6.0] but shl 10, ie. times 1024 var - clr: array[0..2] of double; - hue, max, delta: double; + Red, Green, Blue: longint; + Min, Max, Delta: longint; + Hue: double; begin - clr[0] := ((Color and $ff0000) shr 16)/255; // R - clr[1] := ((Color and $ff00) shr 8)/255; // G - clr[2] := (Color and $ff) /255; // B - max := maxvalue(clr); - delta := max - minvalue(clr); + // extract the colors + // division by 255 is omitted, since it is implicitly done + // when deviding by delta + Red := ((Color and $ff0000) shr 16); // R + Green := ((Color and $ff00) shr 8); // G + Blue := (Color and $ff) ; // B + + Min := Red; + if Green < Min then Min := Green; + if Blue < Min then Min := Blue; + + Max := Red; + if Green > Max then Max := Green; + if Blue > Max then Max := Blue; + // calc hue - if (delta = 0.0) then hue := 0 - else if (clr[0] = max) then hue := (clr[1]-clr[2])/delta - else if (clr[1] = max) then hue := 2.0+(clr[2]-clr[0])/delta - else if (clr[2] = max) then hue := 4.0+(clr[0]-clr[1])/delta; - if (hue < 0.0) then - hue := hue + 6.0; - Result := hue; + Delta := Max - Min; // This gives a type size mismatch warning, because Delta is longword, ie. >= 0 + // But the assignments above are easy enough to be sure, that Max - Min is >= 0. + if (Delta = 0) then + Result := 0 + else + begin + // The division by Delta is done separately afterwards. + // Necessary because Delphi did not do the type conversion from + // longword to double as expected. + // After the change to longint, we may not need it, but left for now + // Something to check + if (Max = Red ) then Hue := Green - Blue + else if (Max = Green) then Hue := 2.0*Delta + Blue - Red + else if (Max = Blue ) then Hue := 4.0*Delta + Red - Green; + Hue := Hue / Delta; + if (Hue < 0.0) then + Hue := Hue + 6.0; + Result := trunc(Hue*1024); // '*1024' is shl 10 + // if NewColor = $000000 then + // Log.LogError ('Hue: ' + FloatToStr(Hue), 'ColorToHue'); + end; end; var - DestinationHue: Double; - PixelIndex: Cardinal; + PixelIndex: longword; Pixel: PByte; PixelColors: PByteArray; - clr: array[0..2] of UInt32; // [0: R, 1: G, 2: B] - hsv: array[0..2] of UInt32; // [0: H(ue), 1: S(aturation), 2: V(alue)] - dhue: UInt32; - h_int: Cardinal; - delta, f, p, q, t: Longint; - max: Uint32; + Red, Green, Blue: longword; + Hue, Sat: longword; + Min, Max, Delta: longword; + HueInteger: longword; + f, p, q, t: longword; + GreyReal: real; + Grey: byte; begin - DestinationHue := col2hue(NewColor); - - dhue := Trunc(DestinationHue*1024); Pixel := ImgSurface^.Pixels; + // check of the size of a pixel in bytes. + // It should be always 4, but this + // additional safeguard will show, + // whether something went wrong up to here. + + if ImgSurface^.format.BytesPerPixel <> 4 then + Log.LogError ('ColorizeImage: The pixel size should be 4, but it is ' + + IntToStr(ImgSurface^.format.BytesPerPixel)); + + // Check whether the new color is white, grey or black, + // because a greyscale must be created in a different + // way. + + Red := ((NewColor and $ff0000) shr 16); // R + Green := ((NewColor and $ff00) shr 8); // G + Blue := (NewColor and $ff) ; // B + + if (Red = Green) and (Green = Blue) then // greyscale image + begin + // According to these recommendations (ITU-R BT.709-5) + // the conversion parameters for rgb to greyscale are + // 0.299, 0.587, 0.114 + for PixelIndex := 0 to (ImgSurface^.W * ImgSurface^.H)-1 do + begin + PixelColors := PByteArray(Pixel); + {$IFDEF FPC_BIG_ENDIAN} + GreyReal := 0.299*PixelColors[3] + 0.587*PixelColors[2] + 0.114*PixelColors[1]; + // PixelColors[0] is alpha and remains untouched + {$ELSE} + GreyReal := 0.299*PixelColors[0] + 0.587*PixelColors[1] + 0.114*PixelColors[2]; + // PixelColors[3] is alpha and remains untouched + {$ENDIF} + Grey := round(GreyReal); + {$IFDEF FPC_BIG_ENDIAN} + PixelColors[3] := Grey; + PixelColors[2] := Grey; + PixelColors[1] := Grey; + // PixelColors[0] is alpha and remains untouched + {$ELSE} + PixelColors[0] := Grey; + PixelColors[1] := Grey; + PixelColors[2] := Grey; + // PixelColors[3] is alpha and remains untouched + {$ENDIF} + Inc(Pixel, ImgSurface^.format.BytesPerPixel); + end; + exit; // we are done with a greyscale image. + end; + + Hue := ColorToHue(NewColor); // Hue is shl 10 + f := Hue and $3ff; // f is the dezimal part of hue + HueInteger := Hue shr 10; + for PixelIndex := 0 to (ImgSurface^.W * ImgSurface^.H)-1 do begin PixelColors := PByteArray(Pixel); // inlined colorize per pixel // uses fixed point math + // shl 10 is used for divisions + // get color values - clr[0] := PixelColors[0] shl 10; - clr[1] := PixelColors[1] shl 10; - clr[2] := PixelColors[2] shl 10; + + {$IFDEF FPC_BIG_ENDIAN} + Red := PixelColors[3]; + Green := PixelColors[2]; + Blue := PixelColors[1]; + // PixelColors[0] is alpha and remains untouched + {$ELSE} + Red := PixelColors[0]; + Green := PixelColors[1]; + Blue := PixelColors[2]; + // PixelColors[3] is alpha and remains untouched + {$ENDIF} + //calculate luminance and saturation from rgb - max := clr[0]; - if clr[1] > max then max := clr[1]; - if clr[2] > max then max := clr[2]; - delta := clr[0]; - if clr[1] < delta then delta := clr[1]; - if clr[2] < delta then delta := clr[2]; - delta := max-delta; - hsv[0] := dhue; // shl 8 - hsv[2] := max; // shl 8 - if (max = 0) then - hsv[1] := 0 + Max := Red; + if Green > Max then Max := Green; + if Blue > Max then Max := Blue ; + + if (Max = 0) then // the color is black + begin + {$IFDEF FPC_BIG_ENDIAN} + PixelColors[3] := 0; + PixelColors[2] := 0; + PixelColors[1] := 0; + {$ELSE} + PixelColors[0] := 0; + PixelColors[1] := 0; + PixelColors[2] := 0; + {$ENDIF} + end else - hsv[1] := (delta shl 10) div max; // shl 8 - h_int := hsv[0] and $fffffC00; - f := hsv[0]-h_int; //shl 10 - p := (hsv[2]*(1024-hsv[1])) shr 10; - q := (hsv[2]*(1024-(hsv[1]*f) shr 10)) shr 10; - t := (hsv[2]*(1024-(hsv[1]*(1024-f)) shr 10)) shr 10; - h_int := h_int shr 10; - case h_int of - 0: begin clr[0] := hsv[2]; clr[1] := t; clr[2] := p; end; // (v,t,p) - 1: begin clr[0] := q; clr[1] := hsv[2]; clr[2] := p; end; // (q,v,p) - 2: begin clr[0] := p; clr[1] := hsv[2]; clr[2] := t; end; // (p,v,t) - 3: begin clr[0] := p; clr[1] := q; clr[2] := hsv[2]; end; // (p,q,v) - 4: begin clr[0] := t; clr[1] := p; clr[2] := hsv[2]; end; // (t,p,v) - 5: begin clr[0] := hsv[2]; clr[1] := p; clr[2] := q; end; // (v,p,q) - end; + begin + Min := Red; + if Green < Min then Min := Green; + if Blue < Min then Min := Blue ; - PixelColors[0] := clr[0] shr 10; - PixelColors[1] := clr[1] shr 10; - PixelColors[2] := clr[2] shr 10; + if (Min = 255) then // the color is white + begin + {$IFDEF FPC_BIG_ENDIAN} + PixelColors[3] := 255; + PixelColors[2] := 255; + PixelColors[1] := 255; + {$ELSE} + PixelColors[0] := 255; + PixelColors[1] := 255; + PixelColors[2] := 255; + {$ENDIF} + end + else // all colors except black and white + begin + Delta := Max - Min; // This gives a type size mismatch warning, because Delta is longword, ie. >= 0 + // But the assignments above are easy enough to be sure, that Max - Min is >= 0. + Sat := (Delta shl 10) div Max; // shl 10 + + // shr 10 corrects that Sat and f are shl 10 + // the resulting p, q and t are unshifted + + p := (Max * (1024 - Sat )) shr 10; + q := (Max * (1024 - (Sat * f ) shr 10)) shr 10; + t := (Max * (1024 - (Sat * (1024 - f)) shr 10)) shr 10; + + // The above 3 lines give type size mismatch warning, but all variables are longword and the ranges should be ok. + + case HueInteger of + 0: begin Red := Max; Green := t; Blue := p; end; // (v,t,p) + 1: begin Red := q; Green := Max; Blue := p; end; // (q,v,p) + 2: begin Red := p; Green := Max; Blue := t; end; // (p,v,t) + 3: begin Red := p; Green := q; Blue := Max; end; // (p,q,v) + 4: begin Red := t; Green := p; Blue := Max; end; // (t,p,v) + 5: begin Red := Max; Green := p; Blue := q; end; // (v,p,q) + end; + + {$IFDEF FPC_BIG_ENDIAN} + PixelColors[3] := byte(Red); + PixelColors[2] := byte(Green); + PixelColors[1] := byte(Blue); + {$ELSE} + PixelColors[0] := byte(Red); + PixelColors[1] := byte(Green); + PixelColors[2] := byte(Blue); + {$ENDIF} + + end; + end; Inc(Pixel, ImgSurface^.format.BytesPerPixel); end; diff --git a/Lua/src/base/UIni.pas b/Lua/src/base/UIni.pas index 3a4d6129..998d19fb 100644 --- a/Lua/src/base/UIni.pas +++ b/Lua/src/base/UIni.pas @@ -36,8 +36,11 @@ interface uses Classes, IniFiles, + SysUtils, ULog, - SysUtils; + UTextEncoding, + UFilesystem, + UPath; type // TInputDeviceConfig stores the configuration for an input device. @@ -70,13 +73,13 @@ type TBackgroundMusicOption = (bmoOff, bmoOn); TIni = class private - function RemoveFileExt(FullName: string): string; function ExtractKeyIndex(const Key, Prefix, Suffix: string): integer; function GetMaxKeyIndex(Keys: TStringList; const Prefix, Suffix: string): integer; - function GetArrayIndex(const SearchArray: array of string; Value: string; CaseInsensitiv: Boolean = False): integer; - function ReadArrayIndex(const SearchArray: array of string; IniFile: TCustomIniFile; + function GetArrayIndex(const SearchArray: array of UTF8String; Value: string; CaseInsensitiv: boolean = false): integer; + function ReadArrayIndex(const SearchArray: array of UTF8String; IniFile: TCustomIniFile; IniSection: string; IniProperty: string; Default: integer): integer; + procedure TranslateOptionValues; procedure LoadInputDeviceCfg(IniFile: TMemIniFile); procedure SaveInputDeviceCfg(IniFile: TIniFile); procedure LoadThemes(IniFile: TCustomIniFile); @@ -84,21 +87,21 @@ type procedure LoadScreenModes(IniFile: TCustomIniFile); public - Name: array[0..11] of string; + Name: array[0..11] of UTF8String; // Templates for Names Mod - NameTeam: array[0..2] of string; - NameTemplate: array[0..11] of string; + NameTeam: array[0..2] of UTF8String; + NameTemplate: array[0..11] of UTF8String; //Filename of the opened iniFile - Filename: string; + Filename: IPath; // Game Players: integer; Difficulty: integer; Language: integer; Tabs: integer; - Tabs_at_startup:integer; //Tabs at Startup fix + TabsAtStartup: integer; //Tabs at Startup fix Sorting: integer; Debug: integer; @@ -106,7 +109,7 @@ type Screens: integer; Resolution: integer; Depth: integer; - VisualizerOption:integer; + VisualizerOption: integer; FullScreen: integer; TextureSize: integer; SingWindow: integer; @@ -121,8 +124,8 @@ type BeatClick: integer; SavePlayback: integer; ThresholdIndex: integer; - AudioOutputBufferSizeIndex:integer; - VoicePassthrough:integer; + AudioOutputBufferSizeIndex: integer; + VoicePassthrough: integer; //Song Preview PreviewVolume: integer; @@ -138,8 +141,8 @@ type Theme: integer; SkinNo: integer; Color: integer; - BackgroundMusicOption:integer; - + BackgroundMusicOption: integer; + // Record InputDeviceConfig: array of TInputDeviceConfig; @@ -154,6 +157,7 @@ type // Controller Joypad: integer; + Mouse: integer; procedure Load(); procedure Save(); @@ -162,117 +166,366 @@ type end; var - Ini: TIni; - IResolution: array of string; - ILanguage: array of string; - ITheme: array of string; - ISkin: array of string; - - + Ini: TIni; + IResolution: array of UTF8String; + ILanguage: array of UTF8String; + ITheme: array of UTF8String; + ISkin: array of UTF8String; const - IPlayers: array[0..4] of string = ('1', '2', '3', '4', '6'); - IPlayersVals: array[0..4] of integer = ( 1 , 2 , 3 , 4 , 6 ); + IPlayers: array[0..4] of UTF8String = ('1', '2', '3', '4', '6'); + IPlayersVals: array[0..4] of integer = ( 1 , 2 , 3 , 4 , 6 ); - IDifficulty: array[0..2] of string = ('Easy', 'Medium', 'Hard'); - ITabs: array[0..1] of string = ('Off', 'On'); + IDifficulty: array[0..2] of UTF8String = ('Easy', 'Medium', 'Hard'); + ITabs: array[0..1] of UTF8String = ('Off', 'On'); - ISorting: array[0..7] of string = ('Edition', 'Genre', 'Language', 'Folder', 'Title', 'Artist', 'Title2', 'Artist2'); + ISorting: array[0..6] of UTF8String = ('Edition', 'Genre', 'Language', 'Folder', 'Title', 'Artist', 'Artist2'); sEdition = 0; sGenre = 1; sLanguage = 2; sFolder = 3; sTitle = 4; sArtist = 5; - sTitle2 = 6; - sArtist2 = 7; - - IDebug: array[0..1] of string = ('Off', 'On'); + sArtist2 = 6; - IScreens: array[0..1] of string = ('1', '2'); - IFullScreen: array[0..1] of string = ('Off', 'On'); - IDepth: array[0..1] of string = ('16 bit', '32 bit'); - IVisualizer: array[0..2] of string = ('Off', 'WhenNoVideo','On'); + IDebug: array[0..1] of UTF8String = ('Off', 'On'); - IBackgroundMusic: array[0..1] of string = ('Off', 'On'); + IScreens: array[0..1] of UTF8String = ('1', '2'); + IFullScreen: array[0..1] of UTF8String = ('Off', 'On'); + IDepth: array[0..1] of UTF8String = ('16 bit', '32 bit'); + IVisualizer: array[0..2] of UTF8String = ('Off', 'WhenNoVideo','On'); + IBackgroundMusic: array[0..1] of UTF8String = ('Off', 'On'); - ITextureSize: array[0..2] of string = ('128', '256', '512'); - ITextureSizeVals: array[0..2] of integer = ( 128, 256, 512); + ITextureSize: array[0..3] of UTF8String = ('64', '128', '256', '512'); + ITextureSizeVals: array[0..3] of integer = ( 64, 128, 256, 512); - ISingWindow: array[0..1] of string = ('Small', 'Big'); + ISingWindow: array[0..1] of UTF8String = ('Small', 'Big'); //SingBar Mod - IOscilloscope: array[0..2] of string = ('Off', 'Osci', 'Bar'); -//IOscilloscope: array[0..1] of string = ('Off', 'On'); + IOscilloscope: array[0..1] of UTF8String = ('Off', 'On'); + + ISpectrum: array[0..1] of UTF8String = ('Off', 'On'); + ISpectrograph: array[0..1] of UTF8String = ('Off', 'On'); + IMovieSize: array[0..2] of UTF8String = ('Half', 'Full [Vid]', 'Full [BG+Vid]'); + + IClickAssist: array[0..1] of UTF8String = ('Off', 'On'); + IBeatClick: array[0..1] of UTF8String = ('Off', 'On'); + ISavePlayback: array[0..1] of UTF8String = ('Off', 'On'); + + IThreshold: array[0..3] of UTF8String = ('5%', '10%', '15%', '20%'); + IThresholdVals: array[0..3] of single = (0.05, 0.10, 0.15, 0.20); + + IVoicePassthrough: array[0..1] of UTF8String = ('Off', 'On'); + + IAudioOutputBufferSize: array[0..9] of UTF8String = ('Auto', '256', '512', '1024', '2048', '4096', '8192', '16384', '32768', '65536'); + IAudioOutputBufferSizeVals: array[0..9] of integer = ( 0, 256, 512 , 1024 , 2048 , 4096 , 8192 , 16384 , 32768 , 65536 ); + + IAudioInputBufferSize: array[0..9] of UTF8String = ('Auto', '256', '512', '1024', '2048', '4096', '8192', '16384', '32768', '65536'); + IAudioInputBufferSizeVals: array[0..9] of integer = ( 0, 256, 512 , 1024 , 2048 , 4096 , 8192 , 16384 , 32768 , 65536 ); + + //Song Preview + IPreviewVolume: array[0..10] of UTF8String = ('Off', '10%', '20%', '30%', '40%', '50%', '60%', '70%', '80%', '90%', '100%'); + IPreviewVolumeVals: array[0..10] of single = ( 0, 0.10, 0.20, 0.30, 0.40, 0.50, 0.60, 0.70, 0.80, 0.90, 1.00 ); + + IPreviewFading: array[0..5] of UTF8String = ('Off', '1 Sec', '2 Secs', '3 Secs', '4 Secs', '5 Secs'); + IPreviewFadingVals: array[0..5] of integer = ( 0, 1, 2, 3, 4, 5 ); + + ILyricsFont: array[0..2] of UTF8String = ('Plain', 'OLine1', 'OLine2'); + ILyricsEffect: array[0..4] of UTF8String = ('Simple', 'Zoom', 'Slide', 'Ball', 'Shift'); + ISolmization: array[0..3] of UTF8String = ('Off', 'Euro', 'Jap', 'American'); + INoteLines: array[0..1] of UTF8String = ('Off', 'On'); - ISpectrum: array[0..1] of string = ('Off', 'On'); - ISpectrograph: array[0..1] of string = ('Off', 'On'); - IMovieSize: array[0..2] of string = ('Half', 'Full [Vid]', 'Full [BG+Vid]'); + IColor: array[0..8] of UTF8String = ('Blue', 'Green', 'Pink', 'Red', 'Violet', 'Orange', 'Yellow', 'Brown', 'Black'); - IClickAssist: array[0..1] of string = ('Off', 'On'); - IBeatClick: array[0..1] of string = ('Off', 'On'); - ISavePlayback: array[0..1] of string = ('Off', 'On'); + // Advanced + ILoadAnimation: array[0..1] of UTF8String = ('Off', 'On'); + IEffectSing: array[0..1] of UTF8String = ('Off', 'On'); + IScreenFade: array[0..1] of UTF8String = ('Off', 'On'); + IAskbeforeDel: array[0..1] of UTF8String = ('Off', 'On'); + IOnSongClick: array[0..2] of UTF8String = ('Sing', 'Select Players', 'Open Menu'); + sStartSing = 0; + sSelectPlayer = 1; + sOpenMenu = 2; + + ILineBonus: array[0..1] of UTF8String = ('Off', 'On'); + IPartyPopup: array[0..1] of UTF8String = ('Off', 'On'); + + IJoypad: array[0..1] of UTF8String = ('Off', 'On'); + IMouse: array[0..2] of UTF8String = ('Off', 'Hardware Cursor', 'Software Cursor'); + + // Recording options + IChannelPlayer: array[0..6] of UTF8String = ('Off', '1', '2', '3', '4', '5', '6'); + IMicBoost: array[0..3] of UTF8String = ('Off', '+6dB', '+12dB', '+18dB'); + +var + ILanguageTranslated: array of UTF8String; - IThreshold: array[0..3] of string = ('5%', '10%', '15%', '20%'); - IThresholdVals: array[0..3] of single = (0.05, 0.10, 0.15, 0.20); + IDifficultyTranslated: array[0..2] of UTF8String = ('Easy', 'Medium', 'Hard'); + ITabsTranslated: array[0..1] of UTF8String = ('Off', 'On'); - IVoicePassthrough: array[0..1] of string = ('Off', 'On'); + ISortingTranslated: array[0..6] of UTF8String = ('Edition', 'Genre', 'Language', 'Folder', 'Title', 'Artist', 'Artist2'); - IAudioOutputBufferSize: array[0..9] of string = ('Auto', '256', '512', '1024', '2048', '4096', '8192', '16384', '32768', '65536'); - IAudioOutputBufferSizeVals: array[0..9] of integer = ( 0, 256, 512 , 1024 , 2048 , 4096 , 8192 , 16384 , 32768 , 65536 ); + IDebugTranslated: array[0..1] of UTF8String = ('Off', 'On'); - IAudioInputBufferSize: array[0..9] of string = ('Auto', '256', '512', '1024', '2048', '4096', '8192', '16384', '32768', '65536'); - IAudioInputBufferSizeVals: array[0..9] of integer = ( 0, 256, 512 , 1024 , 2048 , 4096 , 8192 , 16384 , 32768 , 65536 ); + IFullScreenTranslated: array[0..1] of UTF8String = ('Off', 'On'); + IVisualizerTranslated: array[0..2] of UTF8String = ('Off', 'WhenNoVideo','On'); + + IBackgroundMusicTranslated: array[0..1] of UTF8String = ('Off', 'On'); + ISingWindowTranslated: array[0..1] of UTF8String = ('Small', 'Big'); + + //SingBar Mod + IOscilloscopeTranslated: array[0..1] of UTF8String = ('Off', 'On'); + + ISpectrumTranslated: array[0..1] of UTF8String = ('Off', 'On'); + ISpectrographTranslated: array[0..1] of UTF8String = ('Off', 'On'); + IMovieSizeTranslated: array[0..2] of UTF8String = ('Half', 'Full [Vid]', 'Full [BG+Vid]'); + + IClickAssistTranslated: array[0..1] of UTF8String = ('Off', 'On'); + IBeatClickTranslated: array[0..1] of UTF8String = ('Off', 'On'); + ISavePlaybackTranslated: array[0..1] of UTF8String = ('Off', 'On'); + + IVoicePassthroughTranslated: array[0..1] of UTF8String = ('Off', 'On'); //Song Preview - IPreviewVolume: array[0..10] of string = ('Off', '10%', '20%', '30%', '40%', '50%', '60%', '70%', '80%', '90%', '100%'); - IPreviewVolumeVals: array[0..10] of single = ( 0, 0.10, 0.20, 0.30, 0.40, 0.50, 0.60, 0.70, 0.80, 0.90, 1.00 ); + IPreviewVolumeTranslated: array[0..10] of UTF8String = ('Off', '10%', '20%', '30%', '40%', '50%', '60%', '70%', '80%', '90%', '100%'); + + IAudioOutputBufferSizeTranslated: array[0..9] of UTF8String = ('Auto', '256', '512', '1024', '2048', '4096', '8192', '16384', '32768', '65536'); - IPreviewFading: array[0..5] of string = ('Off', '1 Sec', '2 Secs', '3 Secs', '4 Secs', '5 Secs'); - IPreviewFadingVals: array[0..5] of integer = ( 0, 1, 2, 3, 4, 5 ); + IAudioInputBufferSizeTranslated: array[0..9] of UTF8String = ('Auto', '256', '512', '1024', '2048', '4096', '8192', '16384', '32768', '65536'); + IPreviewFadingTranslated: array[0..5] of UTF8String = ('Off', '1 Sec', '2 Secs', '3 Secs', '4 Secs', '5 Secs'); - ILyricsFont: array[0..2] of string = ('Plain', 'OLine1', 'OLine2'); - ILyricsEffect: array[0..4] of string = ('Simple', 'Zoom', 'Slide', 'Ball', 'Shift'); - ISolmization: array[0..3] of string = ('Off', 'Euro', 'Jap', 'American'); - INoteLines: array[0..1] of string = ('Off', 'On'); + ILyricsFontTranslated: array[0..2] of UTF8String = ('Plain', 'OLine1', 'OLine2'); + ILyricsEffectTranslated: array[0..4] of UTF8String = ('Simple', 'Zoom', 'Slide', 'Ball', 'Shift'); + ISolmizationTranslated: array[0..3] of UTF8String = ('Off', 'Euro', 'Jap', 'American'); + INoteLinesTranslated: array[0..1] of UTF8String = ('Off', 'On'); - IColor: array[0..8] of string = ('Blue', 'Green', 'Pink', 'Red', 'Violet', 'Orange', 'Yellow', 'Brown', 'Black'); + IColorTranslated: array[0..8] of UTF8String = ('Blue', 'Green', 'Pink', 'Red', 'Violet', 'Orange', 'Yellow', 'Brown', 'Black'); // Advanced - ILoadAnimation: array[0..1] of string = ('Off', 'On'); - IEffectSing: array[0..1] of string = ('Off', 'On'); - IScreenFade: array[0..1] of string =('Off', 'On'); - IAskbeforeDel: array[0..1] of string = ('Off', 'On'); - IOnSongClick: array[0..2] of string = ('Sing', 'Select Players', 'Open Menu'); - ILineBonus: array[0..2] of string = ('Off', 'At Score', 'At Notes'); - IPartyPopup: array[0..1] of string = ('Off', 'On'); + ILoadAnimationTranslated: array[0..1] of UTF8String = ('Off', 'On'); + IEffectSingTranslated: array[0..1] of UTF8String = ('Off', 'On'); + IScreenFadeTranslated: array[0..1] of UTF8String = ('Off', 'On'); + IAskbeforeDelTranslated: array[0..1] of UTF8String = ('Off', 'On'); + IOnSongClickTranslated: array[0..2] of UTF8String = ('Sing', 'Select Players', 'Open Menu'); + ILineBonusTranslated: array[0..1] of UTF8String = ('Off', 'On'); + IPartyPopupTranslated: array[0..1] of UTF8String = ('Off', 'On'); - IJoypad: array[0..1] of string = ('Off', 'On'); + IJoypadTranslated: array[0..1] of UTF8String = ('Off', 'On'); + IMouseTranslated: array[0..2] of UTF8String = ('Off', 'Hardware Cursor', 'Software Cursor'); // Recording options - IChannelPlayer: array[0..6] of string = ('Off', '1', '2', '3', '4', '5', '6'); - IMicBoost: array[0..3] of string = ('Off', '+6dB', '+12dB', '+18dB'); + IChannelPlayerTranslated: array[0..6] of UTF8String = ('Off', '1', '2', '3', '4', '5', '6'); + IMicBoostTranslated: array[0..3] of UTF8String = ('Off', '+6dB', '+12dB', '+18dB'); implementation uses StrUtils, - UMain, SDL, + UCommandLine, ULanguage, UPlatform, - USkins, + UMain, URecord, - UCommandLine; + USkins, + UPathUtils, + UUnicodeUtils; (** - * Returns the filename without its fileextension + * Translate and set the values of options, which need translation. *) -function TIni.RemoveFileExt(FullName: string): string; +procedure TIni.TranslateOptionValues; +var + I: integer; begin - Result := ChangeFileExt(FullName, ''); + // Load Languagefile + if (Params.Language <> -1) then + ULanguage.Language.ChangeLanguage(ILanguage[Params.Language]) + else + ULanguage.Language.ChangeLanguage(ILanguage[Ini.Language]); + + SetLength(ILanguageTranslated, Length(ILanguage)); + for I := 0 to High(ILanguage) do + begin + ILanguageTranslated[I] := ULanguage.Language.Translate( + 'OPTION_VALUE_' + UpperCase(ILanguage[I]) + ); + end; + + IDifficultyTranslated[0] := ULanguage.Language.Translate('OPTION_VALUE_EASY'); + IDifficultyTranslated[1] := ULanguage.Language.Translate('OPTION_VALUE_MEDIUM'); + IDifficultyTranslated[2] := ULanguage.Language.Translate('OPTION_VALUE_HARD'); + + ITabsTranslated[0] := ULanguage.Language.Translate('OPTION_VALUE_OFF'); + ITabsTranslated[1] := ULanguage.Language.Translate('OPTION_VALUE_ON'); + + ISortingTranslated[0] := ULanguage.Language.Translate('OPTION_VALUE_EDITION'); + ISortingTranslated[1] := ULanguage.Language.Translate('OPTION_VALUE_GENRE'); + ISortingTranslated[2] := ULanguage.Language.Translate('OPTION_VALUE_LANGUAGE'); + ISortingTranslated[3] := ULanguage.Language.Translate('OPTION_VALUE_FOLDER'); + ISortingTranslated[4] := ULanguage.Language.Translate('OPTION_VALUE_TITLE'); + ISortingTranslated[5] := ULanguage.Language.Translate('OPTION_VALUE_ARTIST'); + ISortingTranslated[6] := ULanguage.Language.Translate('OPTION_VALUE_ARTIST2'); + + IDebugTranslated[0] := ULanguage.Language.Translate('OPTION_VALUE_OFF'); + IDebugTranslated[1] := ULanguage.Language.Translate('OPTION_VALUE_ON'); + + IFullScreenTranslated[0] := ULanguage.Language.Translate('OPTION_VALUE_OFF'); + IFullScreenTranslated[1] := ULanguage.Language.Translate('OPTION_VALUE_ON'); + + IVisualizerTranslated[0] := ULanguage.Language.Translate('OPTION_VALUE_OFF'); + IVisualizerTranslated[1] := ULanguage.Language.Translate('OPTION_VALUE_WHENNOVIDEO'); + IVisualizerTranslated[2] := ULanguage.Language.Translate('OPTION_VALUE_ON'); + + IBackgroundMusicTranslated[0] := ULanguage.Language.Translate('OPTION_VALUE_OFF'); + IBackgroundMusicTranslated[1] := ULanguage.Language.Translate('OPTION_VALUE_ON'); + + ISingWindowTranslated[0] := ULanguage.Language.Translate('OPTION_VALUE_SMALL'); + ISingWindowTranslated[1] := ULanguage.Language.Translate('OPTION_VALUE_BIG'); + + IOscilloscopeTranslated[0] := ULanguage.Language.Translate('OPTION_VALUE_OFF'); + IOscilloscopeTranslated[1] := ULanguage.Language.Translate('OPTION_VALUE_ON'); + + ISpectrumTranslated[0] := ULanguage.Language.Translate('OPTION_VALUE_OFF'); + ISpectrumTranslated[1] := ULanguage.Language.Translate('OPTION_VALUE_ON'); + + ISpectrographTranslated[0] := ULanguage.Language.Translate('OPTION_VALUE_OFF'); + ISpectrographTranslated[1] := ULanguage.Language.Translate('OPTION_VALUE_ON'); + + IMovieSizeTranslated[0] := ULanguage.Language.Translate('OPTION_VALUE_HALF'); + IMovieSizeTranslated[1] := ULanguage.Language.Translate('OPTION_VALUE_FULL_VID'); + IMovieSizeTranslated[2] := ULanguage.Language.Translate('OPTION_VALUE_FULL_VID_BG'); + + IClickAssistTranslated[0] := ULanguage.Language.Translate('OPTION_VALUE_OFF'); + IClickAssistTranslated[1] := ULanguage.Language.Translate('OPTION_VALUE_ON'); + + IBeatClickTranslated[0] := ULanguage.Language.Translate('OPTION_VALUE_OFF'); + IBeatClickTranslated[1] := ULanguage.Language.Translate('OPTION_VALUE_ON'); + + ISavePlaybackTranslated[0] := ULanguage.Language.Translate('OPTION_VALUE_OFF'); + ISavePlaybackTranslated[1] := ULanguage.Language.Translate('OPTION_VALUE_ON'); + + IVoicePassthroughTranslated[0] := ULanguage.Language.Translate('OPTION_VALUE_OFF'); + IVoicePassthroughTranslated[1] := ULanguage.Language.Translate('OPTION_VALUE_ON'); + + ILyricsFontTranslated[0] := ULanguage.Language.Translate('OPTION_VALUE_PLAIN'); + ILyricsFontTranslated[1] := ULanguage.Language.Translate('OPTION_VALUE_OLINE1'); + ILyricsFontTranslated[2] := ULanguage.Language.Translate('OPTION_VALUE_OLINE2'); + + ILyricsEffectTranslated[0] := ULanguage.Language.Translate('OPTION_VALUE_SIMPLE'); + ILyricsEffectTranslated[1] := ULanguage.Language.Translate('OPTION_VALUE_ZOOM'); + ILyricsEffectTranslated[2] := ULanguage.Language.Translate('OPTION_VALUE_SLIDE'); + ILyricsEffectTranslated[3] := ULanguage.Language.Translate('OPTION_VALUE_BALL'); + ILyricsEffectTranslated[4] := ULanguage.Language.Translate('OPTION_VALUE_SHIFT'); + + ISolmizationTranslated[0] := ULanguage.Language.Translate('OPTION_VALUE_OFF'); + ISolmizationTranslated[1] := ULanguage.Language.Translate('OPTION_VALUE_EURO'); + ISolmizationTranslated[2] := ULanguage.Language.Translate('OPTION_VALUE_JAPAN'); + ISolmizationTranslated[3] := ULanguage.Language.Translate('OPTION_VALUE_AMERICAN'); + + INoteLinesTranslated[0] := ULanguage.Language.Translate('OPTION_VALUE_OFF'); + INoteLinesTranslated[1] := ULanguage.Language.Translate('OPTION_VALUE_ON'); + + IColorTranslated[0] := ULanguage.Language.Translate('OPTION_VALUE_BLUE'); + IColorTranslated[1] := ULanguage.Language.Translate('OPTION_VALUE_GREEN'); + IColorTranslated[2] := ULanguage.Language.Translate('OPTION_VALUE_PINK'); + IColorTranslated[3] := ULanguage.Language.Translate('OPTION_VALUE_RED'); + IColorTranslated[4] := ULanguage.Language.Translate('OPTION_VALUE_VIOLET'); + IColorTranslated[5] := ULanguage.Language.Translate('OPTION_VALUE_ORANGE'); + IColorTranslated[6] := ULanguage.Language.Translate('OPTION_VALUE_YELLOW'); + IColorTranslated[7] := ULanguage.Language.Translate('OPTION_VALUE_BROWN'); + IColorTranslated[8] := ULanguage.Language.Translate('OPTION_VALUE_BLACK'); + + // Advanced + ILoadAnimationTranslated[0] := ULanguage.Language.Translate('OPTION_VALUE_OFF'); + ILoadAnimationTranslated[1] := ULanguage.Language.Translate('OPTION_VALUE_ON'); + + IEffectSingTranslated[0] := ULanguage.Language.Translate('OPTION_VALUE_OFF'); + IEffectSingTranslated[1] := ULanguage.Language.Translate('OPTION_VALUE_ON'); + + IScreenFadeTranslated[0] := ULanguage.Language.Translate('OPTION_VALUE_OFF'); + IScreenFadeTranslated[1] := ULanguage.Language.Translate('OPTION_VALUE_ON'); + + IAskbeforeDelTranslated[0] := ULanguage.Language.Translate('OPTION_VALUE_OFF'); + IAskbeforeDelTranslated[1] := ULanguage.Language.Translate('OPTION_VALUE_ON'); + + IOnSongClickTranslated[0] := ULanguage.Language.Translate('OPTION_VALUE_SING'); + IOnSongClickTranslated[1] := ULanguage.Language.Translate('OPTION_VALUE_SELECT_PLAYERS'); + IOnSongClickTranslated[2] := ULanguage.Language.Translate('OPTION_VALUE_OPEN_MENU'); + + ILineBonusTranslated[0] := ULanguage.Language.Translate('OPTION_VALUE_OFF'); + ILineBonusTranslated[1] := ULanguage.Language.Translate('OPTION_VALUE_ON'); + + IPartyPopupTranslated[0] := ULanguage.Language.Translate('OPTION_VALUE_OFF'); + IPartyPopupTranslated[1] := ULanguage.Language.Translate('OPTION_VALUE_ON'); + + IJoypadTranslated[0] := ULanguage.Language.Translate('OPTION_VALUE_OFF'); + IJoypadTranslated[1] := ULanguage.Language.Translate('OPTION_VALUE_ON'); + + IMouseTranslated[0] := ULanguage.Language.Translate('OPTION_VALUE_OFF'); + IMouseTranslated[1] := ULanguage.Language.Translate('OPTION_VALUE_HARDWARE_CURSOR'); + IMouseTranslated[2] := ULanguage.Language.Translate('OPTION_VALUE_SOFTWARE_CURSOR'); + + IAudioOutputBufferSizeTranslated[0] := ULanguage.Language.Translate('OPTION_VALUE_AUTO'); + IAudioOutputBufferSizeTranslated[1] := '256'; + IAudioOutputBufferSizeTranslated[2] := '512'; + IAudioOutputBufferSizeTranslated[3] := '1024'; + IAudioOutputBufferSizeTranslated[4] := '2048'; + IAudioOutputBufferSizeTranslated[5] := '4096'; + IAudioOutputBufferSizeTranslated[6] := '8192'; + IAudioOutputBufferSizeTranslated[7] := '16384'; + IAudioOutputBufferSizeTranslated[8] := '32768'; + IAudioOutputBufferSizeTranslated[9] := '65536'; + + + IAudioInputBufferSizeTranslated[0] := ULanguage.Language.Translate('OPTION_VALUE_AUTO'); + IAudioInputBufferSizeTranslated[1] := '256'; + IAudioInputBufferSizeTranslated[2] := '512'; + IAudioInputBufferSizeTranslated[3] := '1024'; + IAudioInputBufferSizeTranslated[4] := '2048'; + IAudioInputBufferSizeTranslated[5] := '4096'; + IAudioInputBufferSizeTranslated[6] := '8192'; + IAudioInputBufferSizeTranslated[7] := '16384'; + IAudioInputBufferSizeTranslated[8] := '32768'; + IAudioInputBufferSizeTranslated[9] := '65536'; + + //Song Preview + IPreviewVolumeTranslated[0] := ULanguage.Language.Translate('OPTION_VALUE_OFF'); + IPreviewVolumeTranslated[1] := '10%'; + IPreviewVolumeTranslated[2] := '20%'; + IPreviewVolumeTranslated[3] := '30%'; + IPreviewVolumeTranslated[4] := '40%'; + IPreviewVolumeTranslated[5] := '50%'; + IPreviewVolumeTranslated[6] := '60%'; + IPreviewVolumeTranslated[7] := '70%'; + IPreviewVolumeTranslated[8] := '80%'; + IPreviewVolumeTranslated[9] := '90%'; + IPreviewVolumeTranslated[10] := '100%'; + + + IPreviewFadingTranslated[0] := ULanguage.Language.Translate('OPTION_VALUE_OFF'); + IPreviewFadingTranslated[1] := '1 ' + ULanguage.Language.Translate('OPTION_VALUE_SEC'); + IPreviewFadingTranslated[2] := '2 ' + ULanguage.Language.Translate('OPTION_VALUE_SECS'); + IPreviewFadingTranslated[3] := '3 ' + ULanguage.Language.Translate('OPTION_VALUE_SECS'); + IPreviewFadingTranslated[4] := '4 ' + ULanguage.Language.Translate('OPTION_VALUE_SECS'); + IPreviewFadingTranslated[5] := '5 ' + ULanguage.Language.Translate('OPTION_VALUE_SECS'); + + // Recording options + IChannelPlayerTranslated[0] := ULanguage.Language.Translate('OPTION_VALUE_OFF'); + IChannelPlayerTranslated[1] := '1'; + IChannelPlayerTranslated[2] := '2'; + IChannelPlayerTranslated[3] := '3'; + IChannelPlayerTranslated[4] := '4'; + IChannelPlayerTranslated[5] := '5'; + IChannelPlayerTranslated[6] := '6'; + + IMicBoostTranslated[0] := ULanguage.Language.Translate('OPTION_VALUE_OFF'); + IMicBoostTranslated[1] := '+6dB'; + IMicBoostTranslated[2] := '+12dB'; + IMicBoostTranslated[3] := '+18dB'; + end; (** @@ -283,17 +536,22 @@ function TIni.ExtractKeyIndex(const Key, Prefix, Suffix: string): integer; var Value: string; Start: integer; + PrefixPos, SuffixPos: integer; begin Result := -1; - if Pos(Prefix, Key) > -1 then - begin - Start := Pos(Prefix, Key) + Length(Prefix); + PrefixPos := Pos(Prefix, Key); + if (PrefixPos <= 0) then + Exit; + SuffixPos := Pos(Suffix, Key); + if (SuffixPos <= 0) then + Exit; - // copy all between prefix and suffix - Value := Copy(Key, Start, Pos(Suffix, Key)-1 - Start); - Result := StrToIntDef(Value, -1); - end; + Start := PrefixPos + Length(Prefix); + + // copy all between prefix and suffix + Value := Copy(Key, Start, SuffixPos - Start); + Result := StrToIntDef(Value, -1); end; (** @@ -303,7 +561,7 @@ end; *) function TIni.GetMaxKeyIndex(Keys: TStringList; const Prefix, Suffix: string): integer; var - i: integer; + i: integer; KeyIndex: integer; begin Result := -1; @@ -320,8 +578,8 @@ end; * Returns the index of Value in SearchArray * or -1 if Value is not in SearchArray. *) -function TIni.GetArrayIndex(const SearchArray: array of string; Value: string; - CaseInsensitiv: Boolean = False): integer; +function TIni.GetArrayIndex(const SearchArray: array of UTF8String; Value: string; + CaseInsensitiv: boolean = false): integer; var i: integer; begin @@ -344,7 +602,7 @@ end; * If SearchArray does not contain the property value, the default value is * returned. *) -function TIni.ReadArrayIndex(const SearchArray: array of string; IniFile: TCustomIniFile; +function TIni.ReadArrayIndex(const SearchArray: array of UTF8String; IniFile: TCustomIniFile; IniSection: string; IniProperty: string; Default: integer): integer; var StrValue: string; @@ -357,15 +615,14 @@ begin end; end; - procedure TIni.LoadInputDeviceCfg(IniFile: TMemIniFile); var - DeviceCfg: PInputDeviceConfig; - DeviceIndex: integer; + DeviceCfg: PInputDeviceConfig; + DeviceIndex: integer; ChannelCount: integer; ChannelIndex: integer; - RecordKeys: TStringList; - i: integer; + RecordKeys: TStringList; + i: integer; begin RecordKeys := TStringList.Create(); @@ -414,15 +671,15 @@ begin RecordKeys.Free(); // MicBoost - //MicBoost := GetArrayIndex(IMicBoost, IniFile.ReadString('Record', 'MicBoost', 'Off')); + MicBoost := GetArrayIndex(IMicBoost, IniFile.ReadString('Record', 'MicBoost', 'Off')); // Threshold - // ThresholdIndex := GetArrayIndex(IThreshold, IniFile.ReadString('Record', 'Threshold', IThreshold[1])); + ThresholdIndex := GetArrayIndex(IThreshold, IniFile.ReadString('Record', 'Threshold', IThreshold[1])); end; procedure TIni.SaveInputDeviceCfg(IniFile: TIniFile); var - DeviceIndex: integer; - ChannelIndex: integer; + DeviceIndex: integer; + ChannelIndex: integer; begin for DeviceIndex := 0 to High(InputDeviceConfig) do begin @@ -431,7 +688,7 @@ begin InputDeviceConfig[DeviceIndex].Name); IniFile.WriteInteger('Record', Format('Input[%d]', [DeviceIndex+1]), InputDeviceConfig[DeviceIndex].Input); - + // Channel-to-Player Mapping for ChannelIndex := 0 to High(InputDeviceConfig[DeviceIndex].ChannelToPlayerMap) do begin @@ -442,15 +699,15 @@ begin end; // MicBoost - //IniFile.WriteString('Record', 'MicBoost', IMicBoost[MicBoost]); + IniFile.WriteString('Record', 'MicBoost', IMicBoost[MicBoost]); // Threshold - //IniFile.WriteString('Record', 'Threshold', IThreshold[ThresholdIndex]); + IniFile.WriteString('Record', 'Threshold', IThreshold[ThresholdIndex]); end; procedure TIni.LoadPaths(IniFile: TCustomIniFile); var PathStrings: TStringList; - I: integer; + I: integer; begin PathStrings := TStringList.Create; IniFile.ReadSection('Directories', PathStrings); @@ -458,9 +715,9 @@ begin // Load song-paths for I := 0 to PathStrings.Count-1 do begin - if (AnsiStartsText('SongDir', PathStrings[I])) then + if (Pos('SONGDIR', UpperCase(PathStrings[I])) = 1) then begin - AddSongPath(IniFile.ReadString('Directories', PathStrings[I], '')); + AddSongPath(Path(IniFile.ReadString('Directories', PathStrings[I], ''))); end; end; @@ -473,18 +730,23 @@ var ThemeIni: TMemIniFile; ThemeName: string; I: integer; + Iter: IFileIterator; + FileInfo: TFileInfo; begin // Theme SetLength(ITheme, 0); - Log.LogStatus('Searching for Theme : ' + ThemePath + '*.ini', 'Theme'); + Log.LogStatus('Searching for Theme : ' + ThemePath.ToNative + '*.ini', 'Theme'); - FindFirst(ThemePath + '*.ini',faAnyFile, SearchResult); - Repeat - Log.LogStatus('Found Theme: ' + SearchResult.Name, 'Theme'); + + Iter := FileSystem.FileFind(ThemePath.Append('*.ini'), 0); + while (Iter.HasNext) do + begin + FileInfo := Iter.Next; + Log.LogStatus('Found Theme: ' + FileInfo.Name.ToNative, 'Theme'); //Read Themename from Theme - ThemeIni := TMemIniFile.Create(SearchResult.Name); - ThemeName := UpperCase(ThemeIni.ReadString('Theme','Name', RemoveFileExt(SearchResult.Name))); + ThemeIni := TMemIniFile.Create(FileInfo.Name.ToNative); + ThemeName := UpperCase(ThemeIni.ReadString('Theme','Name', FileInfo.Name.SetExtension('').ToNative)); ThemeIni.Free; //Search for Skins for this Theme @@ -493,12 +755,11 @@ begin if UpperCase(Skin.Skin[I].Theme) = ThemeName then begin SetLength(ITheme, Length(ITheme)+1); - ITheme[High(ITheme)] := RemoveFileExt(SearchResult.Name); + ITheme[High(ITheme)] := FileInfo.Name.SetExtension('').ToNative; break; end; end; - until FindNext(SearchResult) <> 0; - FindClose(SearchResult); + end; // No Theme Found if (Length(ITheme) = 0) then @@ -508,7 +769,7 @@ begin Theme := GetArrayIndex(ITheme, IniFile.ReadString('Themes', 'Theme', 'DELUXE'), true); if (Theme = -1) then - Theme := 0; + Theme := 0; // Skin Skin.onThemeChange; @@ -519,7 +780,7 @@ end; procedure TIni.LoadScreenModes(IniFile: TCustomIniFile); // swap two strings - procedure swap(var s1, s2: string); + procedure swap(var s1, s2: UTF8String); var s3: string; begin @@ -530,25 +791,25 @@ procedure TIni.LoadScreenModes(IniFile: TCustomIniFile); var Modes: PPSDL_Rect; - I: integer; + I: integer; begin // Screens Screens := GetArrayIndex(IScreens, IniFile.ReadString('Graphics', 'Screens', IScreens[0])); - + // FullScreen FullScreen := GetArrayIndex(IFullScreen, IniFile.ReadString('Graphics', 'FullScreen', 'On')); // Resolution SetLength(IResolution, 0); - + // Check if there are any modes available // TODO: we should seperate windowed and fullscreen modes. Otherwise it is not // possible to select a reasonable fullscreen mode when in windowed mode if IFullScreen[FullScreen] = 'On' then Modes := SDL_ListModes(nil, SDL_OPENGL or SDL_FULLSCREEN) - else + else Modes := SDL_ListModes(nil, SDL_OPENGL or SDL_RESIZABLE) ; - + if (Modes = nil) then begin Log.LogStatus( 'No resolutions Found' , 'Video'); @@ -567,7 +828,7 @@ begin IResolution[7] := '1440x900'; IResolution[8] := '1600x1200'; IResolution[9] := '1680x1050'; - + Resolution := GetArrayIndex(IResolution, IniFile.ReadString('Graphics', 'Resolution', '800x600')); if Resolution = -1 then begin @@ -587,6 +848,7 @@ begin end; // reverse order + Log.LogStatus( 'Log size of resolution: ' + IntToStr(Length(IResolution)), 'Video'); for I := 0 to (Length(IResolution) div 2) - 1 do begin swap(IResolution[I], IResolution[High(IResolution)-I]); @@ -623,23 +885,19 @@ end; procedure TIni.Load(); var IniFile: TMemIniFile; - I: integer; + I: integer; begin GamePath := Platform.GetGameUserPath; - Log.LogStatus( 'GamePath : ' +GamePath , '' ); + Log.LogStatus( 'GamePath : ' +GamePath.ToNative , '' ); - if (Params.ConfigFile <> '') then - try - FileName := Params.ConfigFile; - except - FileName := GamePath + 'config.ini'; - end + if (Params.ConfigFile.IsSet) then + FileName := Params.ConfigFile else - FileName := GamePath + 'config.ini'; + FileName := GamePath.Append('config.ini'); - Log.LogStatus( 'Using config : ' + FileName , 'Ini'); - IniFile := TMemIniFile.Create( FileName ); + Log.LogStatus('Using config : ' + FileName.ToNative, 'Ini'); + IniFile := TMemIniFile.Create(FileName.ToNative); // Name for I := 0 to 11 do @@ -650,24 +908,23 @@ begin NameTeam[I] := IniFile.ReadString('NameTeam', 'T'+IntToStr(I+1), 'Team'+IntToStr(I+1)); for I := 0 to 11 do NameTemplate[I] := IniFile.ReadString('NameTemplate', 'Name'+IntToStr(I+1), 'Template'+IntToStr(I+1)); - + // Players Players := GetArrayIndex(IPlayers, IniFile.ReadString('Game', 'Players', IPlayers[0])); - + // Difficulty Difficulty := GetArrayIndex(IDifficulty, IniFile.ReadString('Game', 'Difficulty', 'Easy')); - + // Language Language := GetArrayIndex(ILanguage, IniFile.ReadString('Game', 'Language', 'English')); - //Language.ChangeLanguage(ILanguage[Language]); - + // Tabs Tabs := GetArrayIndex(ITabs, IniFile.ReadString('Game', 'Tabs', ITabs[0])); - Tabs_at_startup := Tabs; //Tabs at Startup fix - + TabsAtStartup := Tabs; //Tabs at Startup fix + // Song Sorting Sorting := GetArrayIndex(ISorting, IniFile.ReadString('Game', 'Sorting', ISorting[0])); - + // Debug Debug := GetArrayIndex(IDebug, IniFile.ReadString('Game', 'Debug', IDebug[0])); @@ -680,7 +937,7 @@ begin SingWindow := GetArrayIndex(ISingWindow, IniFile.ReadString('Graphics', 'SingWindow', 'Big')); // Oscilloscope - Oscilloscope := GetArrayIndex(IOscilloscope, IniFile.ReadString('Graphics', 'Oscilloscope', 'Bar')); + Oscilloscope := GetArrayIndex(IOscilloscope, IniFile.ReadString('Graphics', 'Oscilloscope', IOscilloscope[0])); // Spectrum Spectrum := GetArrayIndex(ISpectrum, IniFile.ReadString('Graphics', 'Spectrum', 'Off')); @@ -705,18 +962,18 @@ begin //Preview Volume PreviewVolume := GetArrayIndex(IPreviewVolume, IniFile.ReadString('Sound', 'PreviewVolume', IPreviewVolume[7])); - + //Preview Fading - PreviewFading := GetArrayIndex(IPreviewFading, IniFile.ReadString('Sound', 'PreviewFading', IPreviewFading[1])); + PreviewFading := GetArrayIndex(IPreviewFading, IniFile.ReadString('Sound', 'PreviewFading', IPreviewFading[3])); //AudioRepeat aka VoicePassthrough VoicePassthrough := GetArrayIndex(IVoicePassthrough, IniFile.ReadString('Sound', 'VoicePassthrough', IVoicePassthrough[0])); // Lyrics Font - LyricsFont := GetArrayIndex(ILyricsFont, IniFile.ReadString('Lyrics', 'LyricsFont', ILyricsFont[1])); + LyricsFont := GetArrayIndex(ILyricsFont, IniFile.ReadString('Lyrics', 'LyricsFont', ILyricsFont[0])); // Lyrics Effect - LyricsEffect := GetArrayIndex(ILyricsEffect, IniFile.ReadString('Lyrics', 'LyricsEffect', ILyricsEffect[1])); + LyricsEffect := GetArrayIndex(ILyricsEffect, IniFile.ReadString('Lyrics', 'LyricsEffect', ILyricsEffect[2])); // Solmization Solmization := GetArrayIndex(ISolmization, IniFile.ReadString('Lyrics', 'Solmization', ISolmization[0])); @@ -760,7 +1017,7 @@ begin OnSongClick := GetArrayIndex(IOnSongClick, IniFile.ReadString('Advanced', 'OnSongClick', 'Sing')); // Linebonus - LineBonus := GetArrayIndex(ILineBonus, IniFile.ReadString('Advanced', 'LineBonus', 'At Score')); + LineBonus := GetArrayIndex(ILineBonus, IniFile.ReadString('Advanced', 'LineBonus', ILineBonus[1])); // PartyPopup PartyPopup := GetArrayIndex(IPartyPopup, IniFile.ReadString('Advanced', 'PartyPopup', 'On')); @@ -768,22 +1025,27 @@ begin // Joypad Joypad := GetArrayIndex(IJoypad, IniFile.ReadString('Controller', 'Joypad', IJoypad[0])); + // Mouse + Mouse := GetArrayIndex(IMouse, IniFile.ReadString('Controller', 'Mouse', IMouse[2])); + LoadPaths(IniFile); - + + TranslateOptionValues; + IniFile.Free; end; procedure TIni.Save; var - IniFile: TIniFile; + IniFile: TIniFile; begin - if (FileExists(Filename) and FileIsReadOnly(Filename)) then + if (Filename.IsFile and Filename.IsReadOnly) then begin Log.LogError('Config-file is read-only', 'TIni.Save'); Exit; end; - IniFile := TIniFile.Create(Filename); + IniFile := TIniFile.Create(Filename.ToNative); // Players IniFile.WriteString('Game', 'Players', IPlayers[Players]); @@ -850,7 +1112,7 @@ begin // Song Preview IniFile.WriteString('Sound', 'PreviewVolume', IPreviewVolume[PreviewVolume]); - + // PreviewFading IniFile.WriteString('Sound', 'PreviewFading', IPreviewFading[PreviewFading]); @@ -907,6 +1169,9 @@ begin // Joypad IniFile.WriteString('Controller', 'Joypad', IJoypad[Joypad]); + // Mouse + IniFile.WriteString('Controller', 'Mouse', IMouse[Mouse]); + // Directories (add a template if section is missing) // Note: Value must be ' ' and not '', otherwise no key is generated on Linux if (not IniFile.SectionExists('Directories')) then @@ -920,17 +1185,17 @@ var IniFile: TIniFile; I: integer; begin - if not FileIsReadOnly(Filename) then + if not Filename.IsReadOnly() then begin - IniFile := TIniFile.Create(Filename); + IniFile := TIniFile.Create(Filename.ToNative); //Name Templates for Names Mod - for I := 1 to 12 do - IniFile.WriteString('Name', 'P' + IntToStr(I), Name[I-1]); - for I := 1 to 3 do - IniFile.WriteString('NameTeam', 'T' + IntToStr(I), NameTeam[I-1]); - for I := 1 to 12 do - IniFile.WriteString('NameTemplate', 'Name' + IntToStr(I), NameTemplate[I-1]); + for I := 0 to High(Name) do + IniFile.WriteString('Name', 'P' + IntToStr(I+1), Name[I]); + for I := 0 to High(NameTeam) do + IniFile.WriteString('NameTeam', 'T' + IntToStr(I+1), NameTeam[I]); + for I := 0 to High(NameTemplate) do + IniFile.WriteString('NameTemplate', 'Name' + IntToStr(I+1), NameTemplate[I]); IniFile.Free; end; @@ -940,9 +1205,9 @@ procedure TIni.SaveLevel; var IniFile: TIniFile; begin - if not FileIsReadOnly(Filename) then + if not Filename.IsReadOnly() then begin - IniFile := TIniFile.Create(Filename); + IniFile := TIniFile.Create(Filename.ToNative); // Difficulty IniFile.WriteString('Game', 'Difficulty', IDifficulty[Difficulty]); diff --git a/Lua/src/base/ULanguage.pas b/Lua/src/base/ULanguage.pas index 31840f5f..5f8a2692 100644 --- a/Lua/src/base/ULanguage.pas +++ b/Lua/src/base/ULanguage.pas @@ -33,33 +33,41 @@ interface {$I switches.inc} +uses + UUnicodeUtils; + type TLanguageEntry = record - ID: string; - Text: string; + ID: AnsiString; //**< identifier (ASCII) + Text: UTF8String; //**< translation (UTF-8) end; TLanguageList = record - Name: string; - {FileName: string; } + Name: AnsiString; //**< language name (ASCII) end; + TLanguageEntryArray = array of TLanguageEntry; + TLanguage = class - public - Entry: array of TLanguageEntry; //Entrys of Chosen Language - SEntry: array of TLanguageEntry; //Entrys of Standard Language - CEntry: array of TLanguageEntry; //Constant Entrys e.g. Version - Implode_Glue1, Implode_Glue2: String; - public + private List: array of TLanguageList; - constructor Create; + Entry: TLanguageEntryArray; //**< Entrys of Chosen Language + EntryDefault: TLanguageEntryArray; //**< Entrys of Standard Language + EntryConst: TLanguageEntryArray; //**< Constant Entrys e.g. Version + + Implode_Glue1, Implode_Glue2: UTF8String; + procedure LoadList; - function Translate(Text: String): String; - procedure ChangeLanguage(Language: String); - procedure AddConst(ID, Text: String); - procedure ChangeConst(ID, Text: String); - function Implode(Pieces: Array of String): String; + function FindID(const ID: AnsiString; const EntryList: TLanguageEntryArray): integer; + + public + constructor Create; + function Translate(const Text: RawByteString): UTF8String; + procedure ChangeLanguage(const Language: AnsiString); + procedure AddConst(const ID: AnsiString; const Text: UTF8String); + procedure ChangeConst(const ID: AnsiString; const Text: UTF8String); + function Implode(const Pieces: array of UTF8String): UTF8String; end; var @@ -69,19 +77,18 @@ implementation uses UMain, - // UFiles, UIni, IniFiles, Classes, SysUtils, - {$IFDEF win32} - windows, - {$ENDIF} - ULog; - -//---------- -//Create - Construct Class then LoadList + Standard Language + Set Standard Implode Glues -//---------- + ULog, + UPath, + UFilesystem, + UPathUtils; + +{** + * LoadList, set default language, set standard implode glues + *} constructor TLanguage.Create; var I, J: Integer; @@ -106,9 +113,9 @@ begin begin ChangeLanguage('English'); - SetLength(SEntry, Length(Entry)); - for J := low(Entry) to high(Entry) do - SEntry[J] := Entry[J]; + SetLength(EntryDefault, Length(Entry)); + for J := 0 to high(Entry) do + EntryDefault[J] := Entry[J]; SetLength(Entry, 0); @@ -122,41 +129,44 @@ begin end; -//---------- -//LoadList - Parse the Language Dir searching Translations -//---------- +{** + * Parse the Language Dir searching Translations + *} procedure TLanguage.LoadList; var - SR: TSearchRec; // for parsing directory + Iter: IFileIterator; + IniInfo: TFileInfo; + LangName: string; begin SetLength(List, 0); SetLength(ILanguage, 0); - if FindFirst(LanguagesPath + '*.ini', 0, SR) = 0 then begin - repeat - SetLength(List, Length(List)+1); - SetLength(ILanguage, Length(ILanguage)+1); - SR.Name := ChangeFileExt(SR.Name, ''); + Iter := FileSystem.FileFind(LanguagesPath.Append('*.ini'), 0); + while(Iter.HasNext) do + begin + IniInfo := Iter.Next; + + LangName := IniInfo.Name.SetExtension('').ToUTF8; - List[High(List)].Name := SR.Name; - ILanguage[High(ILanguage)] := SR.Name; + SetLength(List, Length(List)+1); + List[High(List)].Name := LangName; - until FindNext(SR) <> 0; - SysUtils.FindClose(SR); - end; // if FindFirst + SetLength(ILanguage, Length(ILanguage)+1); + ILanguage[High(ILanguage)] := LangName; + end; end; -//---------- -//ChangeLanguage - Load the specified LanguageFile -//---------- -procedure TLanguage.ChangeLanguage(Language: String); +{** + * Load the specified LanguageFile + *} +procedure TLanguage.ChangeLanguage(const Language: AnsiString); var - IniFile: TIniFile; + IniFile: TUnicodeMemIniFile; E: integer; // entry S: TStringList; begin SetLength(Entry, 0); - IniFile := TIniFile.Create(LanguagesPath + Language + '.ini'); + IniFile := TUnicodeMemIniFile.Create(LanguagesPath.Append(Language + '.ini')); S := TStringList.Create; IniFile.ReadSectionValues('Text', S); @@ -176,80 +186,107 @@ begin IniFile.Free; end; -//---------- -//Translate - Translate the Text -//---------- -Function TLanguage.Translate(Text: String): String; +{** + * Find the index of ID an array of language entries. + * @returns the index on success, -1 otherwise. + *} +function TLanguage.FindID(const ID: AnsiString; const EntryList: TLanguageEntryArray): integer; var - E: integer; // entry + Index: integer; begin + for Index := 0 to High(EntryList) do + begin + if ID = EntryList[Index].ID then + begin + Result := Index; + Exit; + end; + end; + Result := -1; +end; + +{** + * Translate the Text. + * If Text is an ID, text will be translated according to the current language + * setting. If Text is not a known ID, it will be returned as is. + * @param Text either an ID or an UTF-8 encoded string + *} +function TLanguage.Translate(const Text: RawByteString): UTF8String; +var + E: integer; // entry + ID: AnsiString; + EntryIndex: integer; +begin + // fallback result in case Text is not a known ID Result := Text; - Text := Uppercase(Result); + + // normalize ID case + ID := UpperCase(Text); + + // Check if ID exists //Const Mod - for E := 0 to high(CEntry) do - if Text = CEntry[E].ID then - begin - Result := CEntry[E].Text; - exit; - end; - //Const Mod End + EntryIndex := FindID(ID, EntryConst); + if (EntryIndex >= 0) then + begin + Result := EntryConst[EntryIndex].Text; + Exit; + end; - for E := 0 to high(Entry) do - if Text = Entry[E].ID then - begin - Result := Entry[E].Text; - exit; - end; + EntryIndex := FindID(ID, Entry); + if (EntryIndex >= 0) then + begin + Result := Entry[EntryIndex].Text; + Exit; + end; //Standard Language (If a Language File is Incomplete) //Then use Standard Language - for E := low(SEntry) to high(SEntry) do - if Text = SEntry[E].ID then - begin - Result := SEntry[E].Text; - Break; - end; - //Standard Language END + EntryIndex := FindID(ID, EntryDefault); + if (EntryIndex >= 0) then + begin + Result := EntryDefault[EntryIndex].Text; + Exit; + end; end; -//---------- -//AddConst - Add a Constant ID that will be Translated but not Loaded from the LanguageFile -//---------- -procedure TLanguage.AddConst (ID, Text: String); +{** + * Add a Constant ID that will be Translated but not Loaded from the LanguageFile + *} +procedure TLanguage.AddConst(const ID: AnsiString; const Text: UTF8String); begin - SetLength (CEntry, Length(CEntry) + 1); - CEntry[high(CEntry)].ID := ID; - CEntry[high(CEntry)].Text := Text; + SetLength (EntryConst, Length(EntryConst) + 1); + EntryConst[high(EntryConst)].ID := ID; + EntryConst[high(EntryConst)].Text := Text; end; -//---------- -//ChangeConst - Change a Constant Value by ID -//---------- -procedure TLanguage.ChangeConst(ID, Text: String); +{** + * Change a Constant Value by ID + *} +procedure TLanguage.ChangeConst(const ID: AnsiString; const Text: UTF8String); var I: Integer; begin - for I := 0 to high(CEntry) do + for I := 0 to high(EntryConst) do begin - if CEntry[I].ID = ID then + if EntryConst[I].ID = ID then begin - CEntry[I].Text := Text; + EntryConst[I].Text := Text; Break; end; end; end; -//---------- -//Implode - Connect an Array of Strings with ' and ' or ', ' to one String -//---------- -function TLanguage.Implode(Pieces: Array of String): String; +{** + * Connect an array of strings with ' and ' or ', ' to one string + *} +function TLanguage.Implode(const Pieces: array of UTF8String): UTF8String; var I: Integer; begin Result := ''; //Go through Pieces - for I := low(Pieces) to high(Pieces) do + for I := 0 to high(Pieces) do begin //Add Value Result := Result + Pieces[I]; diff --git a/Lua/src/base/ULog.pas b/Lua/src/base/ULog.pas index 582120bc..e4ff4862 100644 --- a/Lua/src/base/ULog.pas +++ b/Lua/src/base/ULog.pas @@ -34,7 +34,8 @@ interface {$I switches.inc} uses - Classes; + Classes, + UPath; (* * LOG_LEVEL_[TYPE] defines the "minimum" index for logs of type TYPE. Each @@ -115,7 +116,7 @@ type // voice procedure LogVoice(SoundNr: integer); // buffer - procedure LogBuffer(const buf : Pointer; const bufLength : Integer; const filename : string); + procedure LogBuffer(const buf : Pointer; const bufLength : Integer; const filename : IPath); end; procedure DebugWriteln(const aString: String); @@ -132,7 +133,8 @@ uses UMain, UTime, UCommon, - UCommandLine; + UCommandLine, + UPathUtils; (* * Write to console if in debug mode (Thread-safe). @@ -197,7 +199,7 @@ begin if not BenchmarkFileOpened then begin BenchmarkFileOpened := true; - AssignFile(BenchmarkFile, LogPath + 'Benchmark.log'); + AssignFile(BenchmarkFile, LogPath.Append('Benchmark.log').ToNative); {$I-} Rewrite(BenchmarkFile); if IOResult = 0 then @@ -269,7 +271,7 @@ procedure TLog.LogToFile(const Text: string); begin if (FileOutputEnabled and not LogFileOpened) then begin - AssignFile(LogFile, LogPath + 'Error.log'); + AssignFile(LogFile, LogPath.Append('Error.log').ToNative); {$I-} Rewrite(LogFile); if IOResult = 0 then @@ -398,20 +400,19 @@ end; procedure TLog.LogVoice(SoundNr: integer); var - FS: TFileStream; - FileName: string; + FS: TBinaryFileStream; + Prefix: string; + FileName: IPath; Num: integer; begin for Num := 1 to 9999 do begin - FileName := IntToStr(Num); - while Length(FileName) < 4 do - FileName := '0' + FileName; - FileName := LogPath + 'Voice' + FileName + '.raw'; - if not FileExists(FileName) then + Prefix := Format('Voice%.4d', [Num]); + FileName := LogPath.Append(Prefix + '.raw'); + if not FileName.Exists() then break end; - FS := TFileStream.Create(FileName, fmCreate); + FS := TBinaryFileStream.Create(FileName, fmCreate); AudioInputProcessor.Sound[SoundNr].LogBuffer.Seek(0, soBeginning); FS.CopyFrom(AudioInputProcessor.Sound[SoundNr].LogBuffer, AudioInputProcessor.Sound[SoundNr].LogBuffer.Size); @@ -419,21 +420,19 @@ begin FS.Free; end; -procedure TLog.LogBuffer(const buf: Pointer; const bufLength: Integer; const filename: string); +procedure TLog.LogBuffer(const buf: Pointer; const bufLength: Integer; const filename: IPath); var - f : TFileStream; + f : TBinaryFileStream; begin - f := nil; - try - f := TFileStream.Create( filename, fmCreate); - f.Write( buf^, bufLength); - f.Free; - except - on e : Exception do begin - Log.LogError('TLog.LogBuffer: Failed to log buffer into file "' + filename + '". ErrMsg: ' + e.Message); + f := TBinaryFileStream.Create( filename, fmCreate); + try + f.Write( buf^, bufLength); + finally f.Free; end; + except on e : Exception do + Log.LogError('TLog.LogBuffer: Failed to log buffer into file "' + filename.ToNative + '". ErrMsg: ' + e.Message); end; end; diff --git a/Lua/src/base/ULyrics.pas b/Lua/src/base/ULyrics.pas index 82982981..3f62db9c 100644 --- a/Lua/src/base/ULyrics.pas +++ b/Lua/src/base/ULyrics.pas @@ -52,14 +52,14 @@ type Width: real; // width Start: cardinal; // start of the word in quarters (beats) Length: cardinal; // length of the word in quarters - Text: string; // text + Text: UTF8String; // text Freestyle: boolean; // is freestyle? end; TLyricWordArray = array of TLyricWord; TLyricLine = class public - Text: string; // text + Text: UTF8String; // text Width: real; // width Height: real; // height Words: TLyricWordArray; // words in this line diff --git a/Lua/src/base/UMain.pas b/Lua/src/base/UMain.pas index cb3851d1..7b082c57 100644 --- a/Lua/src/base/UMain.pas +++ b/Lua/src/base/UMain.pas @@ -35,112 +35,16 @@ interface uses SysUtils, - Classes, - SDL, - UMusic, - URecord, - UTime, - UDisplay, - UIni, - ULog, - ULyrics, - UScreenSing, - USong, - ULua, - gl; - -type - PPLayerNote = ^TPlayerNote; - TPlayerNote = record - Start: integer; - Length: integer; - Detect: real; // accurate place, detected in the note - Tone: real; - Perfect: boolean; // true if the note matches the original one, lit the star - Hit: boolean; // true if the note Hits the Line - end; - - PPLayer = ^TPlayer; - TPlayer = record - Name: string; - - // Index in Teaminfo record - TeamID: byte; - PlayerID: byte; - - // Scores - Score: real; - ScoreLine: real; - ScoreGolden: real; - - ScoreInt: integer; - ScoreLineInt: integer; - ScoreGoldenInt: integer; - ScoreTotalInt: integer; - - // LineBonus - ScoreLast: real;//Last Line Score - - // PerfectLineTwinkle (effect) - LastSentencePerfect: boolean; - - HighNote: integer; // index of last note (= High(Note)?) - LengthNote: integer; // number of notes (= Length(Note)?). - Note: array of TPlayerNote; - end; + SDL; var - // Absolute Paths - GamePath: string; - SoundPath: string; - SongPaths: TStringList; - LogPath: string; - ThemePath: string; - SkinsPath: string; - ScreenshotsPath: string; - CoverPaths: TStringList; - LanguagesPath: string; - PluginPath: string; - VisualsPath: string; - FontPath: string; - ResourcesPath: string; - PlayListPath: string; - ScriptPath: string; - - Done: boolean; - // FIXME: ConversionFileName should not be global - ConversionFileName: string; - Restart: boolean; - - // player and music info - Player: array of TPlayer; - PlayersPlay: integer; - - CurrentSong : TSong; - //Lua : Plua_State; - -const - MAX_SONG_SCORE = 10000; // max. achievable points per song - MAX_SONG_LINE_BONUS = 1000; // max. achievable line bonus per song - - -function FindPath(out PathResult: string; const RequestedPath: string; NeedsWritePermission: boolean): boolean; -procedure InitializePaths; -procedure AddSongPath(const Path: string); + Done: boolean; + Restart: boolean; procedure Main; procedure MainLoop; procedure CheckEvents; -procedure Sing(Screen: TScreenSing); -procedure NewSentence(Screen: TScreenSing); -procedure NewBeatClick(Screen: TScreenSing); // executed when on then new beat for click -procedure NewBeatDetect(Screen: TScreenSing); // executed when on then new beat for detection -procedure NewNote(Screen: TScreenSing); // detect note -function GetMidBeat(Time: real): real; -function GetTimeFromBeat(Beat: integer): real; -procedure ClearScores(PlayerNum: integer); - type TMainThreadExecProc = procedure(Data: Pointer); @@ -158,31 +62,34 @@ const *} procedure MainThreadExec(Proc: TMainThreadExecProc; Data: Pointer); - implementation uses Math, - StrUtils, - USongs, - UJoystick, + gl, + UCatCovers, UCommandLine, - ULanguage, - //SDL_ttf, - USkins, + UCommon, + UConfig, UCovers, - UCatCovers, UDataBase, - UPlaylist, + UDisplay, UDLLManager, - UParty, - UConfig, - UCore, - UCommon, UGraphic, UGraphicClasses, - UPluginDefs, + UIni, + UJoystick, + ULanguage, + ULog, + UPathUtils, + UPlaylist, + UMusic, + UBeatTimer, UPlatform, + USkins, + USongs, + UThemes, + UParty, ULuaCore, UHookableEvent, ULuaGl, @@ -191,23 +98,20 @@ uses ULuaTextGL, ULuaParty, ULuaScreenSing, - UThemes; - - - + UTime; procedure Main; var - WndTitle: string; + WindowTitle: string; begin {$IFNDEF Debug} try {$ENDIF} - WndTitle := USDXVersionStr; + WindowTitle := USDXVersionStr; Platform.Init; - if Platform.TerminateIfAlreadyRunning(WndTitle) then + if Platform.TerminateIfAlreadyRunning(WindowTitle) then Exit; // fix floating-point exceptions (FPE) @@ -221,17 +125,18 @@ begin DecimalSeparator := '.'; //------------------------------ - //StartUp - Create Classes and Load Files + // StartUp - create classes and load files //------------------------------ - // Initialize SDL - // Without SDL_INIT_TIMER SDL_GetTicks() might return strange values + // initialize SDL + // without SDL_INIT_TIMER SDL_GetTicks() might return strange values SDL_Init(SDL_INIT_VIDEO or SDL_INIT_TIMER); SDL_EnableUnicode(1); // create luacore first so other classes can register their events LuaCore := TLuaCore.Create; + USTime := TTime.Create; VideoBGTimer := TRelativeTimer.Create; @@ -240,7 +145,7 @@ begin // Log + Benchmark Log := TLog.Create; - Log.Title := WndTitle; + Log.Title := WindowTitle; Log.FileOutputEnabled := not Params.NoLog; Log.BenchmarkStart(0); @@ -251,19 +156,19 @@ begin Log.LogStatus('Load Language', 'Initialization'); Language := TLanguage.Create; - // Add Const Values: + // add const values: Language.AddConst('US_VERSION', USDXVersionStr); Log.BenchmarkEnd(1); Log.LogBenchmark('Loading Language', 1); - { +{ // SDL_ttf (Not used yet, maybe in version 1.5) Log.BenchmarkStart(1); Log.LogStatus('Initialize SDL_ttf', 'Initialization'); TTF_Init(); Log.BenchmarkEnd(1); Log.LogBenchmark('Initializing SDL_ttf', 1); - } +} // Skin Log.BenchmarkStart(1); @@ -278,16 +183,10 @@ begin Ini := TIni.Create; Ini.Load; - //it's possible that this is the first run, create a .ini file if neccessary + // it is possible that this is the first run, create a .ini file if neccessary Log.LogStatus('Write Ini', 'Initialization'); Ini.Save; - // Load Languagefile - if (Params.Language <> -1) then - Language.ChangeLanguage(ILanguage[Params.Language]) - else - Language.ChangeLanguage(ILanguage[Ini.Language]); - Log.BenchmarkEnd(1); Log.LogBenchmark('Loading Ini', 1); @@ -304,7 +203,7 @@ begin // Theme Log.BenchmarkStart(1); Log.LogStatus('Load Themes', 'Initialization'); - Theme := TTheme.Create(ThemePath + ITheme[Ini.Theme] + '.ini', Ini.Color); + Theme := TTheme.Create(ThemePath.Append(ITheme[Ini.Theme] + '.ini'), Ini.Color); Log.BenchmarkEnd(1); Log.LogBenchmark('Loading Themes', 1); @@ -341,18 +240,10 @@ begin Log.BenchmarkEnd(1); Log.LogBenchmark('Loading PluginManager', 1); - {// Party Mode Manager - Log.BenchmarkStart(1); - Log.LogStatus('PartySession Manager', 'Initialization'); - PartySession := TPartySession.Create; //Load PartySession - - Log.BenchmarkEnd(1); - Log.LogBenchmark('Loading PartySession Manager', 1); } - // Graphics Log.BenchmarkStart(1); Log.LogStatus('Initialize 3D', 'Initialization'); - Initialize3D(WndTitle); + Initialize3D(WindowTitle); Log.BenchmarkEnd(1); Log.LogBenchmark('Initializing 3D', 1); @@ -361,10 +252,10 @@ begin Log.LogStatus('DataBase System', 'Initialization'); DataBase := TDataBaseSystem.Create; - if (Params.ScoreFile = '') then - DataBase.Init (Platform.GetGameUserPath + 'Ultrastar.db') + if (Params.ScoreFile.IsUnset) then + DataBase.Init(Platform.GetGameUserPath.Append('Ultrastar.db')) else - DataBase.Init (Params.ScoreFile); + DataBase.Init(Params.ScoreFile); Log.BenchmarkEnd(1); Log.LogBenchmark('Loading DataBase System', 1); @@ -384,7 +275,7 @@ begin Log.LogBenchmark('Loading Particle System', 1); // Joypad - if (Ini.Joypad = 1) OR (Params.Joypad) then + if (Ini.Joypad = 1) or (Params.Joypad) then begin Log.BenchmarkStart(1); Log.LogStatus('Initialize Joystick', 'Initialization'); @@ -394,25 +285,6 @@ begin end; // Lua - {Log.BenchmarkStart(1); - Lua := luaL_newstate; - if Lua = nil then - Log.LogError('Lua init failed','Lua'); - luaL_openlibs(Lua); - - luaopen_gl(Lua); // gl (Lua) - lua_pop(Lua, 1); // remove table from stack - luaopen_Log(Lua); // Log + Benchmark (Lua) - lua_pop(Lua, 1); // remove table from stack - luaopen_TextGL(Lua); // TextGL (Lua) - lua_pop(Lua, 1); // remove table from stack - luaopen_Texture(Lua); // Texture (Lua) - lua_pop(Lua, 1); // remove table from stack - - Log.BenchmarkEnd(1); - Log.LogBenchmark('Initializing Lua', 1); } - - Log.BenchmarkStart(1); Party := TPartyGame.Create; Log.BenchmarkEnd(1); @@ -435,13 +307,25 @@ begin LuaCore.DumpPlugins; - Log.BenchmarkEnd(0); Log.LogBenchmark('Loading Time', 0); + Log.LogStatus('Creating Core', 'Initialization'); +{ + Core := TCore.Create( + USDXShortVersionStr, + MakeVersion(USDX_VERSION_MAJOR, + USDX_VERSION_MINOR, + USDX_VERSION_RELEASE, + chr(0)) + ); +} + + Log.LogStatus('Running Core', 'Initialization'); + //Core.Run; //------------------------------ - //Start- Mainloop + // Start Mainloop //------------------------------ Log.LogStatus('Main Loop', 'Initialization'); MainLoop; @@ -450,20 +334,18 @@ begin finally {$ENDIF} //------------------------------ - //Finish Application + // Finish Application //------------------------------ // TODO: // call an uninitialize routine for every initialize step - // or at least use the corresponding Free-Methods + // or at least use the corresponding Free methods FinalizeMedia(); //TTF_Quit(); SDL_Quit(); - //lua_close(Lua); - if assigned(Log) then begin Log.LogStatus('Main Loop', 'Finished'); @@ -476,7 +358,7 @@ end; procedure MainLoop; var - Delay: integer; + Delay: integer; const MAX_FPS = 100; begin @@ -493,17 +375,7 @@ begin CheckEvents; // display - done := not Display.Draw; - - {// FIXME remove this when the Partymode works - if FileExists(ScriptPath + 'main.lua') then - begin - if 0 <> luaL_dofile(Lua, PAnsiChar(ScriptPath + 'main.lua')) then - begin - Log.LogError(lua_tostring(Lua,-1)); - end; - end; } - + Done := not Display.Draw; SwapBuffers; // delay @@ -526,13 +398,27 @@ begin end; end; +procedure DoQuit; +begin + // if question option is enabled then show exit popup + if (Ini.AskbeforeDel = 1) then + begin + Display.CurrentScreen^.CheckFadeTo(nil,'MSG_QUIT_USDX'); + end + else // if ask-for-exit is disabled then simply exit + begin + Display.Fade := 0; + Display.NextScreenWithCheck := nil; + Display.CheckOK := true; + end; +end; + procedure CheckEvents; var - Event: TSDL_event; + Event: TSDL_event; + mouseDown: boolean; + mouseBtn: integer; begin - if Assigned(Display.NextScreen) then - Exit; - while (SDL_PollEvent(@Event) <> 0) do begin case Event.type_ of @@ -542,17 +428,56 @@ begin Display.NextScreenWithCheck := nil; Display.CheckOK := true; end; - SDL_MOUSEBUTTONDOWN: + + SDL_MOUSEMOTION, SDL_MOUSEBUTTONDOWN, SDL_MOUSEBUTTONUP: begin - { - with Event.button do + if (Ini.Mouse > 0) then begin - if State = SDL_BUTTON_LEFT Then - begin - // + case Event.type_ of + SDL_MOUSEMOTION: + begin + mouseDown := false; + mouseBtn := 0; + end; + SDL_MOUSEBUTTONDOWN: + begin + mouseDown := true; + mouseBtn := Event.button.button; + + if (mouseBtn = SDL_BUTTON_LEFT) or (mouseBtn = SDL_BUTTON_RIGHT) then + Display.OnMouseButton(true); + end; + SDL_MOUSEBUTTONUP: + begin + mouseDown := false; + mouseBtn := Event.button.button; + + if (mouseBtn = SDL_BUTTON_LEFT) or (mouseBtn = SDL_BUTTON_RIGHT) then + Display.OnMouseButton(false); + end; + end; + + Display.MoveCursor(Event.button.X * 800 / Screen.w, + Event.button.Y * 600 / Screen.h); + + if not Assigned(Display.NextScreen) then + begin //drop input when changing screens + if (ScreenPopupError <> nil) and (ScreenPopupError.Visible) then + done := not ScreenPopupError.ParseMouse(mouseBtn, mouseDown, Event.button.x, Event.button.y) + else if (ScreenPopupInfo <> nil) and (ScreenPopupInfo.Visible) then + done := not ScreenPopupInfo.ParseMouse(mouseBtn, mouseDown, Event.button.x, Event.button.y) + else if (ScreenPopupCheck <> nil) and (ScreenPopupCheck.Visible) then + done := not ScreenPopupCheck.ParseMouse(mouseBtn, mouseDown, Event.button.x, Event.button.y) + else + begin + done := not Display.CurrentScreen^.ParseMouse(mouseBtn, mouseDown, Event.button.x, Event.button.y); + + // if screen wants to exit + if done then + DoQuit; + end; end; end; - } end; SDL_VIDEORESIZE: begin @@ -561,7 +486,7 @@ begin // Note: do NOT call SDL_SetVideoMode on Windows and MacOSX here. // This would create a new OpenGL render-context and all texture data // would be invalidated. - // On Linux the mode MUST be resetted, otherwise graphics will be corrupted. + // On Linux the mode MUST be reset, otherwise graphics will be corrupted. {$IF Defined(Linux) or Defined(FreeBSD)} if boolean( Ini.FullScreen ) then SDL_SetVideoMode(ScreenW, ScreenH, (Ini.Depth+1) * 16, SDL_OPENGL or SDL_FULLSCREEN) @@ -571,64 +496,72 @@ begin end; SDL_KEYDOWN: begin + // translate CTRL-A (ASCII 1) - CTRL-Z (ASCII 26) to correct charcodes. + // keysyms (SDLK_A, ...) could be used instead but they ignore the + // current key mapping (if 'a' is pressed on a French keyboard the + // .unicode field will be 'a' and .sym SDLK_Q). + // IMPORTANT: if CTRL is pressed with a key different than 'A'-'Z' SDL + // will set .unicode to 0. There is no possibility to obtain a + // translated charcode. Use keysyms instead. + //if (Event.key.keysym.unicode in [1 .. 26]) then + // Event.key.keysym.unicode := Ord('A') + Event.key.keysym.unicode - 1; + // remap the "keypad enter" key to the "standard enter" key if (Event.key.keysym.sym = SDLK_KP_ENTER) then Event.key.keysym.sym := SDLK_RETURN; - if (Event.key.keysym.sym = SDLK_F11) or - ((Event.key.keysym.sym = SDLK_RETURN) and - ((Event.key.keysym.modifier and KMOD_ALT) <> 0)) then // toggle full screen - begin - Ini.FullScreen := integer( not boolean( Ini.FullScreen ) ); - - // FIXME: SDL_SetVideoMode creates a new OpenGL RC so we have to - // reload all texture data (-> whitescreen bug). - // Only Linux (and FreeBSD) is able to handle screen-switching this way. - {$IF Defined(Linux) or Defined(FreeBSD)} - if boolean( Ini.FullScreen ) then + if not Assigned(Display.NextScreen) then + begin //drop input when changing screens + { to-do : F11 was used for fullscreen toggle, too here + but we also use the key in screenname and some other + screens. It is droped although fullscreen toggle doesn't + even work on windows. + should we add (Event.key.keysym.sym = SDLK_F11) here + anyway? } + if ((Event.key.keysym.sym = SDLK_RETURN) and + ((Event.key.keysym.modifier and KMOD_ALT) <> 0)) then // toggle full screen begin - SDL_SetVideoMode(ScreenW, ScreenH, (Ini.Depth+1) * 16, SDL_OPENGL or SDL_FULLSCREEN); - SDL_ShowCursor(0); - end - else - begin - SDL_SetVideoMode(ScreenW, ScreenH, (Ini.Depth+1) * 16, SDL_OPENGL or SDL_RESIZABLE); - SDL_ShowCursor(1); - end; + Ini.FullScreen := integer( not boolean( Ini.FullScreen ) ); - glViewPort(0, 0, ScreenW, ScreenH); - {$IFEND} - end - // if print is pressed -> make screenshot and save to screenshot path - else if (Event.key.keysym.sym = SDLK_SYSREQ) or (Event.key.keysym.sym = SDLK_PRINT) then - Display.SaveScreenShot - // if there is a visible popup then let it handle input instead of underlying screen - // shoud be done in a way to be sure the topmost popup has preference (maybe error, then check) - else if (ScreenPopupError <> nil) and (ScreenPopupError.Visible) then - done := not ScreenPopupError.ParseInput(Event.key.keysym.sym, WideChar(Event.key.keysym.unicode), true) - else if (ScreenPopupCheck <> nil) and (ScreenPopupCheck.Visible) then - done := not ScreenPopupCheck.ParseInput(Event.key.keysym.sym, WideChar(Event.key.keysym.unicode), true) - else - begin - // check if screen wants to exit - done := not Display.CurrentScreen^.ParseInput(Event.key.keysym.sym, WideChar(Event.key.keysym.unicode), true); - - // if screen wants to exit - if done then - begin - // if question option is enabled then show exit popup - if (Ini.AskbeforeDel = 1) then + // FIXME: SDL_SetVideoMode creates a new OpenGL RC so we have to + // reload all texture data (-> whitescreen bug). + // Only Linux and FreeBSD are able to handle screen-switching this way. + {$IF Defined(Linux) or Defined(FreeBSD)} + if boolean( Ini.FullScreen ) then begin - Display.CurrentScreen^.CheckFadeTo(nil,'MSG_QUIT_USDX'); + SDL_SetVideoMode(ScreenW, ScreenH, (Ini.Depth+1) * 16, SDL_OPENGL or SDL_FULLSCREEN); end - else // if ask-for-exit is disabled then simply exit + else begin - Display.Fade := 0; - Display.NextScreenWithCheck := nil; - Display.CheckOK := true; + SDL_SetVideoMode(ScreenW, ScreenH, (Ini.Depth+1) * 16, SDL_OPENGL or SDL_RESIZABLE); end; - end; + Display.SetCursor; + + glViewPort(0, 0, ScreenW, ScreenH); + {$IFEND} + end + // if print is pressed -> make screenshot and save to screenshot path + else if (Event.key.keysym.sym = SDLK_SYSREQ) or (Event.key.keysym.sym = SDLK_PRINT) then + Display.SaveScreenShot + // if there is a visible popup then let it handle input instead of underlying screen + // shoud be done in a way to be sure the topmost popup has preference (maybe error, then check) + else if (ScreenPopupError <> nil) and (ScreenPopupError.Visible) then + Done := not ScreenPopupError.ParseInput(Event.key.keysym.sym, Event.key.keysym.unicode, true) + else if (ScreenPopupInfo <> nil) and (ScreenPopupInfo.Visible) then + Done := not ScreenPopupInfo.ParseInput(Event.key.keysym.sym, Event.key.keysym.unicode, true) + else if (ScreenPopupCheck <> nil) and (ScreenPopupCheck.Visible) then + Done := not ScreenPopupCheck.ParseInput(Event.key.keysym.sym, Event.key.keysym.unicode, true) + else + begin + // check if screen wants to exit + Done := not Display.ParseInput(Event.key.keysym.sym, Event.key.keysym.unicode, true); + + // if screen wants to exit + if Done then + DoQuit; + + end; end; end; SDL_JOYAXISMOTION: @@ -662,574 +595,4 @@ begin SDL_PushEvent(@Event); end; -function GetTimeForBeats(BPM, Beats: real): real; -begin - Result := 60 / BPM * Beats; -end; - -function GetBeats(BPM, msTime: real): real; -begin - Result := BPM * msTime / 60; -end; - -procedure GetMidBeatSub(BPMNum: integer; var Time: real; var CurBeat: real); -var - NewTime: real; -begin - if High(CurrentSong.BPM) = BPMNum then - begin - // last BPM - CurBeat := CurrentSong.BPM[BPMNum].StartBeat + GetBeats(CurrentSong.BPM[BPMNum].BPM, Time); - Time := 0; - end - else - begin - // not last BPM - // count how much time is it for start of the new BPM and store it in NewTime - NewTime := GetTimeForBeats(CurrentSong.BPM[BPMNum].BPM, CurrentSong.BPM[BPMNum+1].StartBeat - CurrentSong.BPM[BPMNum].StartBeat); - - // compare it to remaining time - if (Time - NewTime) > 0 then - begin - // there is still remaining time - CurBeat := CurrentSong.BPM[BPMNum].StartBeat; - Time := Time - NewTime; - end - else - begin - // there is no remaining time - CurBeat := CurrentSong.BPM[BPMNum].StartBeat + GetBeats(CurrentSong.BPM[BPMNum].BPM, Time); - Time := 0; - end; // if - end; // if -end; - -function GetMidBeat(Time: real): real; -var - CurBeat: real; - CurBPM: integer; -begin - // static BPM - if Length(CurrentSong.BPM) = 1 then - begin - Result := Time * CurrentSong.BPM[0].BPM / 60; - end - // variable BPM - else if Length(CurrentSong.BPM) > 1 then - begin - CurBeat := 0; - CurBPM := 0; - while (Time > 0) do - begin - GetMidBeatSub(CurBPM, Time, CurBeat); - Inc(CurBPM); - end; - - Result := CurBeat; - end - // invalid BPM - else - begin - Result := 0; - end; -end; - -function GetTimeFromBeat(Beat: integer): real; -var - CurBPM: integer; -begin - // static BPM - if Length(CurrentSong.BPM) = 1 then - begin - Result := CurrentSong.GAP / 1000 + Beat * 60 / CurrentSong.BPM[0].BPM; - end - // variable BPM - else if Length(CurrentSong.BPM) > 1 then - begin - Result := CurrentSong.GAP / 1000; - CurBPM := 0; - while (CurBPM <= High(CurrentSong.BPM)) and - (Beat > CurrentSong.BPM[CurBPM].StartBeat) do - begin - if (CurBPM < High(CurrentSong.BPM)) and - (Beat >= CurrentSong.BPM[CurBPM+1].StartBeat) then - begin - // full range - Result := Result + (60 / CurrentSong.BPM[CurBPM].BPM) * - (CurrentSong.BPM[CurBPM+1].StartBeat - CurrentSong.BPM[CurBPM].StartBeat); - end; - - if (CurBPM = High(CurrentSong.BPM)) or - (Beat < CurrentSong.BPM[CurBPM+1].StartBeat) then - begin - // in the middle - Result := Result + (60 / CurrentSong.BPM[CurBPM].BPM) * - (Beat - CurrentSong.BPM[CurBPM].StartBeat); - end; - Inc(CurBPM); - end; - - { - while (Time > 0) do - begin - GetMidBeatSub(CurBPM, Time, CurBeat); - Inc(CurBPM); - end; - } - end - // invalid BPM - else - begin - Result := 0; - end; -end; - -procedure Sing(Screen: TScreenSing); -var - Count: integer; - CountGr: integer; - CP: integer; - N: integer; -begin - LyricsState.UpdateBeats(); - - // sentences routines - for CountGr := 0 to 0 do //High(Lines) - begin; - CP := CountGr; - // old parts - LyricsState.OldLine := Lines[CP].Current; - - // choose current parts - for Count := 0 to Lines[CP].High do - begin - if LyricsState.CurrentBeat >= Lines[CP].Line[Count].Start then - Lines[CP].Current := Count; - end; - - // clean player note if there is a new line - // (optimization on halfbeat time) - if Lines[CP].Current <> LyricsState.OldLine then - NewSentence(Screen); - - end; // for CountGr - - // make some operations on clicks - if {(LyricsState.CurrentBeatC >= 0) and }(LyricsState.OldBeatC <> LyricsState.CurrentBeatC) then - NewBeatClick(Screen); - - // make some operations when detecting new voice pitch - if (LyricsState.CurrentBeatD >= 0) and (LyricsState.OldBeatD <> LyricsState.CurrentBeatD) then - NewBeatDetect(Screen); -end; - -procedure NewSentence(Screen: TScreenSing); -var - i: integer; -begin - // clean note of player - for i := 0 to High(Player) do - begin - Player[i].LengthNote := 0; - Player[i].HighNote := -1; - SetLength(Player[i].Note, 0); - end; - - // on sentence change... - Screen.onSentenceChange(Lines[0].Current); -end; - -procedure NewBeatClick; -var - Count: integer; -begin - // beat click - if ((Ini.BeatClick = 1) and - ((LyricsState.CurrentBeatC + Lines[0].Resolution + Lines[0].NotesGAP) mod Lines[0].Resolution = 0)) then - begin - AudioPlayback.PlaySound(SoundLib.Click); - end; - - for Count := 0 to Lines[0].Line[Lines[0].Current].HighNote do - begin - if (Lines[0].Line[Lines[0].Current].Note[Count].Start = LyricsState.CurrentBeatC) then - begin - // click assist - if Ini.ClickAssist = 1 then - AudioPlayback.PlaySound(SoundLib.Click); - - // drum machine - (* - TempBeat := LyricsState.CurrentBeat;// + 2; - if (TempBeat mod 8 = 0) then Music.PlayDrum; - if (TempBeat mod 8 = 4) then Music.PlayClap; - //if (TempBeat mod 4 = 2) then Music.PlayHihat; - if (TempBeat mod 4 <> 0) then Music.PlayHihat; - *) - end; - end; -end; - -procedure NewBeatDetect(Screen: TScreenSing); -begin - NewNote(Screen); -end; - -procedure NewNote(Screen: TScreenSing); -var - LineFragmentIndex: integer; - CurrentLineFragment: PLineFragment; - PlayerIndex: integer; - CurrentSound: TCaptureBuffer; - CurrentPlayer: PPlayer; - LastPlayerNote: PPlayerNote; - Line: PLine; - SentenceIndex: integer; - SentenceMin: integer; - SentenceMax: integer; - SentenceDetected: integer; // sentence of detected note - NoteAvailable: boolean; - NewNote: boolean; - Range: integer; - NoteHit: boolean; - MaxSongPoints: integer; // max. points for the song (without line bonus) - MaxLinePoints: real; // max. points for the current line -begin - // TODO: add duet mode support - // use Lines[LineSetIndex] with LineSetIndex depending on the current player - - // count min and max sentence range for checking (detection is delayed to the notes we see on the screen) - SentenceMin := Lines[0].Current-1; - if (SentenceMin < 0) then - SentenceMin := 0; - SentenceMax := Lines[0].Current; - - // check for an active note at the current time defined in the lyrics - NoteAvailable := false; - SentenceDetected := SentenceMin; - for SentenceIndex := SentenceMin to SentenceMax do - begin - Line := @Lines[0].Line[SentenceIndex]; - for LineFragmentIndex := 0 to Line.HighNote do - begin - CurrentLineFragment := @Line.Note[LineFragmentIndex]; - // check if line is active - if ((CurrentLineFragment.Start <= LyricsState.CurrentBeatD) and - (CurrentLineFragment.Start + CurrentLineFragment.Length-1 >= LyricsState.CurrentBeatD)) and - (CurrentLineFragment.NoteType <> ntFreestyle) and // but ignore FreeStyle notes - (CurrentLineFragment.Length > 0) then // and make sure the note lengths is at least 1 - begin - SentenceDetected := SentenceIndex; - NoteAvailable := true; - Break; - end; - end; - // TODO: break here, if NoteAvailable is true? We would then use the first instead - // of the last note matching the current beat if notes overlap. But notes - // should not overlap at all. - //if (NoteAvailable) then - // Break; - end; - - // analyze player signals - for PlayerIndex := 0 to PlayersPlay-1 do - begin - CurrentPlayer := @Player[PlayerIndex]; - CurrentSound := AudioInputProcessor.Sound[PlayerIndex]; - - // At the beginning of the song there is no previous note - if (Length(CurrentPlayer.Note) > 0) then - LastPlayerNote := @CurrentPlayer.Note[CurrentPlayer.HighNote] - else - LastPlayerNote := nil; - - // analyze buffer - CurrentSound.AnalyzeBuffer; - - // add some noise - // TODO: do we need this? - //LyricsState.Tone := LyricsState.Tone + Round(Random(3)) - 1; - - // add note if possible - if (CurrentSound.ToneValid and NoteAvailable) then - begin - Line := @Lines[0].Line[SentenceDetected]; - - // process until last note - for LineFragmentIndex := 0 to Line.HighNote do - begin - CurrentLineFragment := @Line.Note[LineFragmentIndex]; - if (CurrentLineFragment.Start <= LyricsState.OldBeatD+1) and - (CurrentLineFragment.Start + CurrentLineFragment.Length > LyricsState.OldBeatD+1) then - begin - // compare notes (from song-file and from player) - - // move players tone to proper octave - while (CurrentSound.Tone - CurrentLineFragment.Tone > 6) do - CurrentSound.Tone := CurrentSound.Tone - 12; - - while (CurrentSound.Tone - CurrentLineFragment.Tone < -6) do - CurrentSound.Tone := CurrentSound.Tone + 12; - - // half size notes patch - NoteHit := false; - - //if Ini.Difficulty = 0 then Range := 2; - //if Ini.Difficulty = 1 then Range := 1; - //if Ini.Difficulty = 2 then Range := 0; - Range := 2 - Ini.Difficulty; - - // check if the player hit the correct tone within the tolerated range - if (Abs(CurrentLineFragment.Tone - CurrentSound.Tone) <= Range) then - begin - // adjust the players tone to the correct one - // TODO: do we need to do this? - CurrentSound.Tone := CurrentLineFragment.Tone; - - // half size notes patch - NoteHit := true; - - if (Ini.LineBonus > 0) then - MaxSongPoints := MAX_SONG_SCORE - MAX_SONG_LINE_BONUS - else - MaxSongPoints := MAX_SONG_SCORE; - - // Note: ScoreValue is the sum of all note values of the song - MaxLinePoints := MaxSongPoints / Lines[0].ScoreValue; - - // FIXME: is this correct? Why do we add the points for a whole line - // if just one note is correct? - case CurrentLineFragment.NoteType of - ntNormal: CurrentPlayer.Score := CurrentPlayer.Score + MaxLinePoints; - ntGolden: CurrentPlayer.ScoreGolden := CurrentPlayer.ScoreGolden + MaxLinePoints; - end; - - CurrentPlayer.ScoreInt := Floor(CurrentPlayer.Score / 10) * 10; - CurrentPlayer.ScoreGoldenInt := Floor(CurrentPlayer.ScoreGolden / 10) * 10; - - CurrentPlayer.ScoreTotalInt := CurrentPlayer.ScoreInt + - CurrentPlayer.ScoreGoldenInt + - CurrentPlayer.ScoreLineInt; - end; - - end; // operation - end; // for - - // check if we have to add a new note or extend the note's length - if (SentenceDetected = SentenceMax) then - begin - // we will add a new note - NewNote := true; - - // if previous note (if any) was the same, extend prrevious note - if ((CurrentPlayer.LengthNote > 0) and - (LastPlayerNote <> nil) and - (LastPlayerNote.Tone = CurrentSound.Tone) and - ((LastPlayerNote.Start + LastPlayerNote.Length) = LyricsState.CurrentBeatD)) then - begin - NewNote := false; - end; - - // if is not as new note to control - for LineFragmentIndex := 0 to Line.HighNote do - begin - if (Line.Note[LineFragmentIndex].Start = LyricsState.CurrentBeatD) then - NewNote := true; - end; - - // add new note - if NewNote then - begin - // new note - Inc(CurrentPlayer.LengthNote); - Inc(CurrentPlayer.HighNote); - SetLength(CurrentPlayer.Note, CurrentPlayer.LengthNote); - - // update player's last note - LastPlayerNote := @CurrentPlayer.Note[CurrentPlayer.HighNote]; - with LastPlayerNote^ do - begin - Start := LyricsState.CurrentBeatD; - Length := 1; - Tone := CurrentSound.Tone; // Tone || ToneAbs - Detect := LyricsState.MidBeat; - Hit := NoteHit; // half note patch - end; - end - else - begin - // extend note length - if (LastPlayerNote <> nil) then - Inc(LastPlayerNote.Length); - end; - - // check for perfect note and then lit the star (on Draw) - for LineFragmentIndex := 0 to Line.HighNote do - begin - CurrentLineFragment := @Line.Note[LineFragmentIndex]; - if (CurrentLineFragment.Start = LastPlayerNote.Start) and - (CurrentLineFragment.Length = LastPlayerNote.Length) and - (CurrentLineFragment.Tone = LastPlayerNote.Tone) then - begin - LastPlayerNote.Perfect := true; - end; - end; - end; // if SentenceDetected = SentenceMax - - end; // if Detected - end; // for PlayerIndex - - //Log.LogStatus('EndBeat', 'NewBeat'); - - // on sentence end -> for LineBonus and display of SingBar (rating pop-up) - if (SentenceDetected >= Low(Lines[0].Line)) and - (SentenceDetected <= High(Lines[0].Line)) then - begin - Line := @Lines[0].Line[SentenceDetected]; - CurrentLineFragment := @Line.Note[Line.HighNote]; - if ((CurrentLineFragment.Start + CurrentLineFragment.Length - 1) = LyricsState.CurrentBeatD) then - begin - if assigned(Screen) then - Screen.OnSentenceEnd(SentenceDetected); - end; - end; - -end; - -procedure ClearScores(PlayerNum: integer); -begin - with Player[PlayerNum] do - begin - Score := 0; - ScoreLine := 0; - ScoreGolden := 0; - - ScoreInt := 0; - ScoreLineInt := 0; - ScoreGoldenInt:= 0; - ScoreTotalInt := 0; - - ScoreLast := 0; - - LastSentencePerfect := false; - end; -end; - -procedure AddSpecialPath(var PathList: TStringList; const Path: string); -var - I: integer; - PathAbs, OldPathAbs: string; -begin - if (PathList = nil) then - PathList := TStringList.Create; - - if (Path = '') or not ForceDirectories(Path) then - Exit; - - PathAbs := IncludeTrailingPathDelimiter(ExpandFileName(Path)); - - // check if path or a part of the path was already added - for I := 0 to PathList.Count-1 do - begin - OldPathAbs := IncludeTrailingPathDelimiter(ExpandFileName(PathList[I])); - // check if the new directory is a sub-directory of a previously added one. - // This is also true, if both paths point to the same directories. - if (AnsiStartsText(OldPathAbs, PathAbs)) then - begin - // ignore the new path - Exit; - end; - - // check if a previously added directory is a sub-directory of the new one. - if (AnsiStartsText(PathAbs, OldPathAbs)) then - begin - // replace the old with the new one. - PathList[I] := PathAbs; - Exit; - end; - end; - - PathList.Add(PathAbs); -end; - -procedure AddSongPath(const Path: string); -begin - AddSpecialPath(SongPaths, Path); -end; - -procedure AddCoverPath(const Path: string); -begin - AddSpecialPath(CoverPaths, Path); -end; - -(** - * Initialize a path variable - * After setting paths, make sure that paths exist - *) -function FindPath(out PathResult: string; const RequestedPath: string; NeedsWritePermission: boolean): boolean; -begin - Result := false; - - if (RequestedPath = '') then - Exit; - - // Make sure the directory exists - if (not ForceDirectories(RequestedPath)) then - begin - PathResult := ''; - Exit; - end; - - PathResult := IncludeTrailingPathDelimiter(RequestedPath); - - if (NeedsWritePermission) and - (FileIsReadOnly(RequestedPath)) then - begin - Exit; - end; - - Result := true; -end; - -(** - * Function sets all absolute paths e.g. song path and makes sure the directorys exist - *) -procedure InitializePaths; -begin - // Log directory (must be writable) - if (not FindPath(LogPath, Platform.GetLogPath, true)) then - begin - Log.FileOutputEnabled := false; - Log.LogWarn('Log directory "'+ Platform.GetLogPath +'" not available', 'InitializePaths'); - end; - - FindPath(SoundPath, Platform.GetGameSharedPath + 'sounds', false); - FindPath(ThemePath, Platform.GetGameSharedPath + 'themes', false); - FindPath(SkinsPath, Platform.GetGameSharedPath + 'themes', false); - FindPath(LanguagesPath, Platform.GetGameSharedPath + 'languages', false); - FindPath(PluginPath, Platform.GetGameSharedPath + 'plugins', false); - FindPath(VisualsPath, Platform.GetGameSharedPath + 'visuals', false); - FindPath(FontPath, Platform.GetGameSharedPath + 'fonts', false); - FindPath(ResourcesPath, Platform.GetGameSharedPath + 'resources', false); - FindPath(ScriptPath, Platform.GetGameSharedPath + 'scripts', false); - - // Playlists are not shared as we need one directory to write too - FindPath(PlaylistPath, Platform.GetGameUserPath + 'playlists', true); - - // Screenshot directory (must be writable) - if (not FindPath(ScreenshotsPath, Platform.GetGameUserPath + 'screenshots', true)) then - begin - Log.LogWarn('Screenshot directory "'+ Platform.GetGameUserPath +'" not available', 'InitializePaths'); - end; - - // Add song paths - AddSongPath(Params.SongPath); - AddSongPath(Platform.GetGameSharedPath + 'songs'); - AddSongPath(Platform.GetGameUserPath + 'songs'); - - // Add category cover paths - AddCoverPath(Platform.GetGameSharedPath + 'covers'); - AddCoverPath(Platform.GetGameUserPath + 'covers'); -end; - end. diff --git a/Lua/src/base/UMusic.pas b/Lua/src/base/UMusic.pas index 792d5e3f..5d816c9a 100644 --- a/Lua/src/base/UMusic.pas +++ b/Lua/src/base/UMusic.pas @@ -34,12 +34,24 @@ interface {$I switches.inc} uses + SysUtils, + Classes, UTime, - Classes; + UBeatTimer, + UPath; type TNoteType = (ntFreestyle, ntNormal, ntGolden); +const + // ScoreFactor defines how a notehit of a specified notetype is + // measured in comparison to the other types + // 0 means this notetype is not rated at all + // 2 means a hit of this notetype will be rated w/ twice as much + // points as a hit of a notetype w/ ScoreFactor 1 + ScoreFactor: array[TNoteType] of integer = (0, 1, 2); + +type (** * TLineFragment represents a fragment of a lyrics line. * This is a text-fragment (e.g. a syllable) assigned to a note pitch, @@ -51,7 +63,7 @@ type Start: integer; // beat the fragment starts at Length: integer; // length in beats Tone: integer; // full range tone - Text: string; // text assigned to this fragment (a syllable, word, etc.) + Text: UTF8String; // text assigned to this fragment (a syllable, word, etc.) NoteType: TNoteType; // note-type: golden-note/freestyle etc. end; @@ -62,8 +74,8 @@ type PLine = ^TLine; TLine = record Start: integer; // the start beat of this line (<> start beat of the first note of this line) - Lyric: string; - LyricWidth: real; // @deprecated: width of the line in pixels. + Lyric: UTF8String; + //LyricWidth: real; // @deprecated: width of the line in pixels. // Do not use this as the width is not correct. // Use TLyricsEngine.GetUpperLine().Width instead. End_: integer; @@ -81,7 +93,7 @@ type *) TLines = record Current: integer; // for drawing of current line - High: integer; // (= High(Line)?) + High: integer; // = High(Line)! Number: integer; Resolution: integer; NotesGAP: integer; @@ -89,51 +101,6 @@ type Line: array of TLine; end; - (** - * TLyricsState contains all information concerning the - * state of the lyrics, e.g. the current beat or duration of the lyrics. - *) - TLyricsState = class - private - Timer: TRelativeTimer; // keeps track of the current time - public - OldBeat: integer; // previous discovered beat - CurrentBeat: integer; // current beat (rounded) - MidBeat: real; // current beat (float) - - // now we use this for super synchronization! - // only used when analyzing voice - // TODO: change ...D to ...Detect(ed) - OldBeatD: integer; // previous discovered beat - CurrentBeatD: integer; // current discovered beat (rounded) - MidBeatD: real; // current discovered beat (float) - - // we use this for audible clicks - // TODO: Change ...C to ...Click - OldBeatC: integer; // previous discovered beat - CurrentBeatC: integer; - MidBeatC: real; // like CurrentBeatC - - OldLine: integer; // previous displayed sentence - - StartTime: real; // time till start of lyrics (= Gap) - TotalTime: real; // total song time - - constructor Create(); - procedure Pause(); - procedure Resume(); - - procedure Reset(); - procedure UpdateBeats(); - - (** - * current song time (in seconds) used as base-timer for lyrics etc. - *) - function GetCurrentTime(): real; - procedure SetCurrentTime(Time: real); - end; - - const FFTSize = 512; // size of FFT data (output: FFTSize/2 values) type @@ -212,12 +179,12 @@ type TSoundEffect = class public EngineData: Pointer; // can be used for engine-specific data - procedure Callback(Buffer: PChar; BufSize: integer); virtual; abstract; + procedure Callback(Buffer: PByteArray; BufSize: integer); virtual; abstract; end; TVoiceRemoval = class(TSoundEffect) public - procedure Callback(Buffer: PChar; BufSize: integer); override; + procedure Callback(Buffer: PByteArray; BufSize: integer); override; end; type @@ -262,7 +229,7 @@ type function IsEOF(): boolean; virtual; abstract; function IsError(): boolean; virtual; abstract; public - function ReadData(Buffer: PChar; BufferSize: integer): integer; virtual; abstract; + function ReadData(Buffer: PByteArray; BufferSize: integer): integer; virtual; abstract; property EOF: boolean read IsEOF; property Error: boolean read IsError; @@ -292,7 +259,7 @@ type function GetVolume(): single; virtual; abstract; procedure SetVolume(Volume: single); virtual; abstract; function Synchronize(BufferSize: integer; FormatInfo: TAudioFormatInfo): integer; - procedure FillBufferWithFrame(Buffer: PChar; BufferSize: integer; Frame: PChar; FrameSize: integer); + procedure FillBufferWithFrame(Buffer: PByteArray; BufferSize: integer; Frame: PByteArray; FrameSize: integer); public (** * Opens a SourceStream for playback. @@ -335,7 +302,7 @@ type function Open(ChannelMap: integer; FormatInfo: TAudioFormatInfo): boolean; virtual; procedure Close(); override; - procedure WriteData(Buffer: PChar; BufferSize: integer); virtual; abstract; + procedure WriteData(Buffer: PByteArray; BufferSize: integer); virtual; abstract; function GetAudioFormatInfo(): TAudioFormatInfo; override; function GetLength(): real; override; @@ -349,7 +316,7 @@ type // soundcard output-devices information TAudioOutputDevice = class public - Name: string; // soundcard name + Name: UTF8String; // soundcard name end; TAudioOutputDeviceList = array of TAudioOutputDevice; @@ -358,7 +325,7 @@ type ['{63A5EBC3-3F4D-4F23-8DFB-B5165FCE33DD}'] function GetName: String; - function Open(const Filename: string): boolean; // true if succeed + function Open(const Filename: IPath): boolean; // true if succeed procedure Close; procedure Play; @@ -410,7 +377,7 @@ type // nil-pointers is not neccessary anymore. // PlaySound/StopSound will be removed then, OpenSound will be renamed to // CreateSound. - function OpenSound(const Filename: String): TAudioPlaybackStream; + function OpenSound(const Filename: IPath): TAudioPlaybackStream; procedure PlaySound(Stream: TAudioPlaybackStream); procedure StopSound(Stream: TAudioPlaybackStream); @@ -425,7 +392,7 @@ type IGenericDecoder = Interface ['{557B0E9A-604D-47E4-B826-13769F3E10B7}'] - function GetName(): String; + function GetName(): string; function InitializeDecoder(): boolean; function FinalizeDecoder(): boolean; //function IsSupported(const Filename: string): boolean; @@ -434,13 +401,13 @@ type (* IVideoDecoder = Interface( IGenericDecoder ) ['{2F184B2B-FE69-44D5-9031-0A2462391DCA}'] - function Open(const Filename: string): TVideoDecodeStream; + function Open(const Filename: IPath): TVideoDecodeStream; end; *) IAudioDecoder = Interface( IGenericDecoder ) ['{AB47B1B6-2AA9-4410-BF8C-EC79561B5478}'] - function Open(const Filename: string): TAudioDecodeStream; + function Open(const Filename: IPath): TAudioDecodeStream; end; IAudioInput = Interface @@ -468,7 +435,7 @@ type * input-buffer bytes used. * Returns the number of bytes written to the output-buffer or -1 if an error occured. *) - function Convert(InputBuffer: PChar; OutputBuffer: PChar; var InputSize: integer): integer; virtual; abstract; + function Convert(InputBuffer: PByteArray; OutputBuffer: PByteArray; var InputSize: integer): integer; virtual; abstract; (** * Destination/Source size ratio @@ -490,7 +457,7 @@ const SOUNDID_CLICK = 5; LAST_SOUNDID = SOUNDID_CLICK; - BaseSoundFilenames: array[0..LAST_SOUNDID] of string = ( + BaseSoundFilenames: array[0..LAST_SOUNDID] of IPath = ( '%SOUNDPATH%/Common start.mp3', // Start '%SOUNDPATH%/Common back.mp3', // Back '%SOUNDPATH%/menu swoosh.mp3', // Swoosh @@ -531,7 +498,7 @@ type procedure StartBgMusic(); procedure PauseBgMusic(); // TODO - //function AddSound(Filename: string): integer; + //function AddSound(Filename: IPath): integer; //procedure RemoveSound(ID: integer); //function GetSound(ID: integer): TAudioPlaybackStream; //property Sound[ID: integer]: TAudioPlaybackStream read GetSound; default; @@ -561,13 +528,13 @@ procedure DumpMediaInterfaces(); implementation uses - sysutils, math, UIni, - UMain, + UNote, UCommandLine, URecord, - ULog; + ULog, + UPathUtils; var DefaultVideoPlayback : IVideoPlayback; @@ -688,7 +655,7 @@ begin FilterInterfaceList(IAudioDecoder, MediaManager, InterfaceList); for i := 0 to InterfaceList.Count-1 do begin - CurrentAudioDecoder := IAudioDecoder(InterfaceList[i]); + CurrentAudioDecoder := InterfaceList[i] as IAudioDecoder; if (not CurrentAudioDecoder.InitializeDecoder()) then begin Log.LogError('Initialize failed, Removing - '+ CurrentAudioDecoder.GetName); @@ -705,7 +672,7 @@ begin FilterInterfaceList(IAudioPlayback, MediaManager, InterfaceList); for i := 0 to InterfaceList.Count-1 do begin - CurrentAudioPlayback := IAudioPlayback(InterfaceList[i]); + CurrentAudioPlayback := InterfaceList[i] as IAudioPlayback; if (CurrentAudioPlayback.InitializePlayback()) then begin DefaultAudioPlayback := CurrentAudioPlayback; @@ -720,7 +687,7 @@ begin FilterInterfaceList(IAudioInput, MediaManager, InterfaceList); for i := 0 to InterfaceList.Count-1 do begin - CurrentAudioInput := IAudioInput(InterfaceList[i]); + CurrentAudioInput := InterfaceList[i] as IAudioInput; if (CurrentAudioInput.InitializeRecord()) then begin DefaultAudioInput := CurrentAudioInput; @@ -753,7 +720,7 @@ begin FilterInterfaceList(IVideoPlayback, MediaManager, InterfaceList); for i := 0 to InterfaceList.Count-1 do begin - VideoInterface := IVideoPlayback(InterfaceList[i]); + VideoInterface := InterfaceList[i] as IVideoPlayback; if (VideoInterface.Init()) then begin DefaultVideoPlayback := VideoInterface; @@ -768,7 +735,7 @@ begin FilterInterfaceList(IVideoVisualization, MediaManager, InterfaceList); for i := 0 to InterfaceList.Count-1 do begin - VisualInterface := IVideoVisualization(InterfaceList[i]); + VisualInterface := InterfaceList[i] as IVideoVisualization; if (VisualInterface.Init()) then begin DefaultVisualization := VisualInterface; @@ -782,7 +749,7 @@ begin // now that we have all interfaces, we can dump them // TODO: move this to another place - if FindCmdLineSwitch( cMediaInterfaces ) then + if FindCmdLineSwitch(cMediaInterfaces) then begin DumpMediaInterfaces(); halt; @@ -806,27 +773,27 @@ begin // finalize audio playback interfaces (should be done before the decoders) FilterInterfaceList(IAudioPlayback, MediaManager, InterfaceList); for i := 0 to InterfaceList.Count-1 do - IAudioPlayback(InterfaceList[i]).FinalizePlayback(); + (InterfaceList[i] as IAudioPlayback).FinalizePlayback(); // finalize audio input interfaces FilterInterfaceList(IAudioInput, MediaManager, InterfaceList); for i := 0 to InterfaceList.Count-1 do - IAudioInput(InterfaceList[i]).FinalizeRecord(); + (InterfaceList[i] as IAudioInput).FinalizeRecord(); // finalize audio decoder interfaces FilterInterfaceList(IAudioDecoder, MediaManager, InterfaceList); for i := 0 to InterfaceList.Count-1 do - IAudioDecoder(InterfaceList[i]).FinalizeDecoder(); + (InterfaceList[i] as IAudioDecoder).FinalizeDecoder(); // finalize video interfaces FilterInterfaceList(IVideoPlayback, MediaManager, InterfaceList); for i := 0 to InterfaceList.Count-1 do - IVideoPlayback(InterfaceList[i]).Finalize(); + (InterfaceList[i] as IVideoPlayback).Finalize(); // finalize audio decoder interfaces FilterInterfaceList(IVideoVisualization, MediaManager, InterfaceList); for i := 0 to InterfaceList.Count-1 do - IVideoVisualization(InterfaceList[i]).Finalize(); + (InterfaceList[i] as IVideoVisualization).Finalize(); InterfaceList.Free; @@ -889,14 +856,14 @@ procedure TSoundLibrary.LoadSounds(); begin UnloadSounds(); - Start := AudioPlayback.OpenSound(SoundPath + 'Common start.mp3'); - Back := AudioPlayback.OpenSound(SoundPath + 'Common back.mp3'); - Swoosh := AudioPlayback.OpenSound(SoundPath + 'menu swoosh.mp3'); - Change := AudioPlayback.OpenSound(SoundPath + 'select music change music 50.mp3'); - Option := AudioPlayback.OpenSound(SoundPath + 'option change col.mp3'); - Click := AudioPlayback.OpenSound(SoundPath + 'rimshot022b.mp3'); + Start := AudioPlayback.OpenSound(SoundPath.Append('Common start.mp3')); + Back := AudioPlayback.OpenSound(SoundPath.Append('Common back.mp3')); + Swoosh := AudioPlayback.OpenSound(SoundPath.Append('menu swoosh.mp3')); + Change := AudioPlayback.OpenSound(SoundPath.Append('select music change music 50.mp3')); + Option := AudioPlayback.OpenSound(SoundPath.Append('option change col.mp3')); + Click := AudioPlayback.OpenSound(SoundPath.Append('rimshot022b.mp3')); - BGMusic := AudioPlayback.OpenSound(SoundPath + 'Bebeto_-_Loop010.mp3'); + BGMusic := AudioPlayback.OpenSound(SoundPath.Append('Bebeto_-_Loop010.mp3')); if (BGMusic <> nil) then BGMusic.Loop := True; @@ -942,7 +909,7 @@ end; { TVoiceRemoval } -procedure TVoiceRemoval.Callback(Buffer: PChar; BufSize: integer); +procedure TVoiceRemoval.Callback(Buffer: PByteArray; BufSize: integer); var FrameIndex, FrameSize: integer; Value: integer; @@ -967,92 +934,6 @@ begin end; end; - -{ TVoiceRemoval } - -constructor TLyricsState.Create(); -begin - // create a triggered timer, so we can Pause() it, set the time - // and Resume() it afterwards for better synching. - Timer := TRelativeTimer.Create(true); - - // reset state - Reset(); -end; - -procedure TLyricsState.Pause(); -begin - Timer.Pause(); -end; - -procedure TLyricsState.Resume(); -begin - Timer.Resume(); -end; - -procedure TLyricsState.SetCurrentTime(Time: real); -begin - // do not start the timer (if not started already), - // after setting the current time - Timer.SetTime(Time, false); -end; - -function TLyricsState.GetCurrentTime(): real; -begin - Result := Timer.GetTime(); -end; - -(** - * Resets the timer and state of the lyrics. - * The timer will be stopped afterwards so you have to call Resume() - * to start the lyrics timer. - *) -procedure TLyricsState.Reset(); -begin - Pause(); - SetCurrentTime(0); - - StartTime := 0; - TotalTime := 0; - - OldBeat := -1; - MidBeat := -1; - CurrentBeat := -1; - - OldBeatC := -1; - MidBeatC := -1; - CurrentBeatC := -1; - - OldBeatD := -1; - MidBeatD := -1; - CurrentBeatD := -1; -end; - -(** - * Updates the beat information (CurrentBeat/MidBeat/...) according to the - * current lyric time. - *) -procedure TLyricsState.UpdateBeats(); -var - CurLyricsTime: real; -begin - CurLyricsTime := GetCurrentTime(); - - OldBeat := CurrentBeat; - MidBeat := GetMidBeat(CurLyricsTime - StartTime / 1000); - CurrentBeat := Floor(MidBeat); - - OldBeatC := CurrentBeatC; - MidBeatC := GetMidBeat(CurLyricsTime - StartTime / 1000); - CurrentBeatC := Floor(MidBeatC); - - OldBeatD := CurrentBeatD; - // MidBeatD = MidBeat with additional GAP - MidBeatD := -0.5 + GetMidBeat(CurLyricsTime - (StartTime + 120 + 20) / 1000); - CurrentBeatD := Floor(MidBeatD); -end; - - { TAudioConverter } function TAudioConverter.Init(SrcFormatInfo: TAudioFormatInfo; DstFormatInfo: TAudioFormatInfo): boolean; @@ -1180,7 +1061,7 @@ end; (* * Fills a buffer with copies of the given frame or with 0 if frame. *) -procedure TAudioPlaybackStream.FillBufferWithFrame(Buffer: PChar; BufferSize: integer; Frame: PChar; FrameSize: integer); +procedure TAudioPlaybackStream.FillBufferWithFrame(Buffer: PByteArray; BufferSize: integer; Frame: PByteArray; FrameSize: integer); var i: integer; FrameCopyCount: integer; diff --git a/Lua/src/base/UNote.pas b/Lua/src/base/UNote.pas new file mode 100644 index 00000000..8e5b709a --- /dev/null +++ b/Lua/src/base/UNote.pas @@ -0,0 +1,591 @@ +{* 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 UNote; + +interface + +{$IFDEF FPC} + {$MODE Delphi} +{$ENDIF} + +{$I switches.inc} + +uses + SysUtils, + Classes, + SDL, + UMusic, + URecord, + UTime, + UDisplay, + UIni, + ULog, + ULyrics, + UScreenSing, + USong, + gl; + +type + PPLayerNote = ^TPlayerNote; + TPlayerNote = record + Start: integer; + Length: integer; + Detect: real; // accurate place, detected in the note + Tone: real; + Perfect: boolean; // true if the note matches the original one, light the star + Hit: boolean; // true if the note hits the line + end; + + PPLayer = ^TPlayer; + TPlayer = record + Name: UTF8String; + + // Index in Teaminfo record + TeamID: byte; + PlayerID: byte; + + // Scores + Score: real; + ScoreLine: real; + ScoreGolden: real; + + ScoreInt: integer; + ScoreLineInt: integer; + ScoreGoldenInt: integer; + ScoreTotalInt: integer; + + // LineBonus + ScoreLast: real; // Last Line Score + + // PerfectLineTwinkle (effect) + LastSentencePerfect: boolean; + + HighNote: integer; // index of last note (= High(Note)?) + LengthNote: integer; // number of notes (= Length(Note)?). + Note: array of TPlayerNote; + end; + +var + + // player and music info + Player: array of TPlayer; + PlayersPlay: integer; + + CurrentSong: TSong; + +const + MAX_SONG_SCORE = 10000; // max. achievable points per song + MAX_SONG_LINE_BONUS = 1000; // max. achievable line bonus per song + +procedure Sing(Screen: TScreenSing); +procedure NewSentence(Screen: TScreenSing); +procedure NewBeatClick(Screen: TScreenSing); // executed when on then new beat for click +procedure NewBeatDetect(Screen: TScreenSing); // executed when on then new beat for detection +procedure NewNote(Screen: TScreenSing); // detect note +function GetMidBeat(Time: real): real; +function GetTimeFromBeat(Beat: integer): real; + +implementation + +uses + Math, + StrUtils, + USongs, + UJoystick, + UCommandLine, + ULanguage, + //SDL_ttf, + USkins, + UCovers, + UCatCovers, + UDataBase, + UPlaylist, + UDLLManager, + UParty, + UConfig, + UCommon, + UGraphic, + UGraphicClasses, + UPathUtils, + UPlatform, + UThemes; + +function GetTimeForBeats(BPM, Beats: real): real; +begin + Result := 60 / BPM * Beats; +end; + +function GetBeats(BPM, msTime: real): real; +begin + Result := BPM * msTime / 60; +end; + +procedure GetMidBeatSub(BPMNum: integer; var Time: real; var CurBeat: real); +var + NewTime: real; +begin + if High(CurrentSong.BPM) = BPMNum then + begin + // last BPM + CurBeat := CurrentSong.BPM[BPMNum].StartBeat + GetBeats(CurrentSong.BPM[BPMNum].BPM, Time); + Time := 0; + end + else + begin + // not last BPM + // count how much time is it for start of the new BPM and store it in NewTime + NewTime := GetTimeForBeats(CurrentSong.BPM[BPMNum].BPM, CurrentSong.BPM[BPMNum+1].StartBeat - CurrentSong.BPM[BPMNum].StartBeat); + + // compare it to remaining time + if (Time - NewTime) > 0 then + begin + // there is still remaining time + CurBeat := CurrentSong.BPM[BPMNum].StartBeat; + Time := Time - NewTime; + end + else + begin + // there is no remaining time + CurBeat := CurrentSong.BPM[BPMNum].StartBeat + GetBeats(CurrentSong.BPM[BPMNum].BPM, Time); + Time := 0; + end; // if + end; // if +end; + +function GetMidBeat(Time: real): real; +var + CurBeat: real; + CurBPM: integer; +begin + // static BPM + if Length(CurrentSong.BPM) = 1 then + begin + Result := Time * CurrentSong.BPM[0].BPM / 60; + end + // variable BPM + else if Length(CurrentSong.BPM) > 1 then + begin + CurBeat := 0; + CurBPM := 0; + while (Time > 0) do + begin + GetMidBeatSub(CurBPM, Time, CurBeat); + Inc(CurBPM); + end; + + Result := CurBeat; + end + // invalid BPM + else + begin + Result := 0; + end; +end; + +function GetTimeFromBeat(Beat: integer): real; +var + CurBPM: integer; +begin + // static BPM + if Length(CurrentSong.BPM) = 1 then + begin + Result := CurrentSong.GAP / 1000 + Beat * 60 / CurrentSong.BPM[0].BPM; + end + // variable BPM + else if Length(CurrentSong.BPM) > 1 then + begin + Result := CurrentSong.GAP / 1000; + CurBPM := 0; + while (CurBPM <= High(CurrentSong.BPM)) and + (Beat > CurrentSong.BPM[CurBPM].StartBeat) do + begin + if (CurBPM < High(CurrentSong.BPM)) and + (Beat >= CurrentSong.BPM[CurBPM+1].StartBeat) then + begin + // full range + Result := Result + (60 / CurrentSong.BPM[CurBPM].BPM) * + (CurrentSong.BPM[CurBPM+1].StartBeat - CurrentSong.BPM[CurBPM].StartBeat); + end; + + if (CurBPM = High(CurrentSong.BPM)) or + (Beat < CurrentSong.BPM[CurBPM+1].StartBeat) then + begin + // in the middle + Result := Result + (60 / CurrentSong.BPM[CurBPM].BPM) * + (Beat - CurrentSong.BPM[CurBPM].StartBeat); + end; + Inc(CurBPM); + end; + + { + while (Time > 0) do + begin + GetMidBeatSub(CurBPM, Time, CurBeat); + Inc(CurBPM); + end; + } + end + // invalid BPM + else + begin + Result := 0; + end; +end; + +procedure Sing(Screen: TScreenSing); +var + Count: integer; + CountGr: integer; + CP: integer; +begin + LyricsState.UpdateBeats(); + + // sentences routines + for CountGr := 0 to 0 do //High(Lines) + begin; + CP := CountGr; + // old parts + LyricsState.OldLine := Lines[CP].Current; + + // choose current parts + for Count := 0 to Lines[CP].High do + begin + if LyricsState.CurrentBeat >= Lines[CP].Line[Count].Start then + Lines[CP].Current := Count; + end; + + // clean player note if there is a new line + // (optimization on halfbeat time) + if Lines[CP].Current <> LyricsState.OldLine then + NewSentence(Screen); + + end; // for CountGr + + // make some operations on clicks + if {(LyricsState.CurrentBeatC >= 0) and }(LyricsState.OldBeatC <> LyricsState.CurrentBeatC) then + NewBeatClick(Screen); + + // make some operations when detecting new voice pitch + if (LyricsState.CurrentBeatD >= 0) and (LyricsState.OldBeatD <> LyricsState.CurrentBeatD) then + NewBeatDetect(Screen); +end; + +procedure NewSentence(Screen: TScreenSing); +var + i: integer; +begin + // clean note of player + for i := 0 to High(Player) do + begin + Player[i].LengthNote := 0; + Player[i].HighNote := -1; + SetLength(Player[i].Note, 0); + end; + + // on sentence change... + Screen.onSentenceChange(Lines[0].Current); +end; + +procedure NewBeatClick; +var + Count: integer; +begin + // beat click + if ((Ini.BeatClick = 1) and + ((LyricsState.CurrentBeatC + Lines[0].Resolution + Lines[0].NotesGAP) mod Lines[0].Resolution = 0)) then + begin + AudioPlayback.PlaySound(SoundLib.Click); + end; + + for Count := 0 to Lines[0].Line[Lines[0].Current].HighNote do + begin + if (Lines[0].Line[Lines[0].Current].Note[Count].Start = LyricsState.CurrentBeatC) then + begin + // click assist + if Ini.ClickAssist = 1 then + AudioPlayback.PlaySound(SoundLib.Click); + + // drum machine + (* + TempBeat := LyricsState.CurrentBeat; // + 2; + if (TempBeat mod 8 = 0) then Music.PlayDrum; + if (TempBeat mod 8 = 4) then Music.PlayClap; + //if (TempBeat mod 4 = 2) then Music.PlayHihat; + if (TempBeat mod 4 <> 0) then Music.PlayHihat; + *) + end; + end; +end; + +procedure NewBeatDetect(Screen: TScreenSing); +begin + NewNote(Screen); +end; + +procedure NewNote(Screen: TScreenSing); +var + LineFragmentIndex: integer; + CurrentLineFragment: PLineFragment; + PlayerIndex: integer; + CurrentSound: TCaptureBuffer; + CurrentPlayer: PPlayer; + LastPlayerNote: PPlayerNote; + Line: PLine; + SentenceIndex: integer; + SentenceMin: integer; + SentenceMax: integer; + SentenceDetected: integer; // sentence of detected note + NoteAvailable: boolean; + NewNote: boolean; + Range: integer; + NoteHit: boolean; + MaxSongPoints: integer; // max. points for the song (without line bonus) + CurNotePoints: real; // Points for the cur. Note (PointsperNote * ScoreFactor[CurNote]) +begin + // TODO: add duet mode support + // use Lines[LineSetIndex] with LineSetIndex depending on the current player + + // count min and max sentence range for checking + // (detection is delayed to the notes we see on the screen) + SentenceMin := Lines[0].Current-1; + if (SentenceMin < 0) then + SentenceMin := 0; + SentenceMax := Lines[0].Current; + + // check for an active note at the current time defined in the lyrics + NoteAvailable := false; + SentenceDetected := SentenceMin; + for SentenceIndex := SentenceMin to SentenceMax do + begin + Line := @Lines[0].Line[SentenceIndex]; + for LineFragmentIndex := 0 to Line.HighNote do + begin + CurrentLineFragment := @Line.Note[LineFragmentIndex]; + // check if line is active + if ((CurrentLineFragment.Start <= LyricsState.CurrentBeatD) and + (CurrentLineFragment.Start + CurrentLineFragment.Length-1 >= LyricsState.CurrentBeatD)) and + (CurrentLineFragment.NoteType <> ntFreestyle) and // but ignore FreeStyle notes + (CurrentLineFragment.Length > 0) then // and make sure the note length is at least 1 + begin + SentenceDetected := SentenceIndex; + NoteAvailable := true; + Break; + end; + end; + // TODO: break here, if NoteAvailable is true? We would then use the first instead + // of the last note matching the current beat if notes overlap. But notes + // should not overlap at all. + // if (NoteAvailable) then + // Break; + end; + + // analyze player signals + for PlayerIndex := 0 to PlayersPlay-1 do + begin + CurrentPlayer := @Player[PlayerIndex]; + CurrentSound := AudioInputProcessor.Sound[PlayerIndex]; + + // at the beginning of the song there is no previous note + if (Length(CurrentPlayer.Note) > 0) then + LastPlayerNote := @CurrentPlayer.Note[CurrentPlayer.HighNote] + else + LastPlayerNote := nil; + + // analyze buffer + CurrentSound.AnalyzeBuffer; + + // add some noise + // TODO: do we need this? + //LyricsState.Tone := LyricsState.Tone + Round(Random(3)) - 1; + + // add note if possible + if (CurrentSound.ToneValid and NoteAvailable) then + begin + Line := @Lines[0].Line[SentenceDetected]; + + // process until last note + for LineFragmentIndex := 0 to Line.HighNote do + begin + CurrentLineFragment := @Line.Note[LineFragmentIndex]; + if (CurrentLineFragment.Start <= LyricsState.OldBeatD+1) and + (CurrentLineFragment.Start + CurrentLineFragment.Length > LyricsState.OldBeatD+1) then + begin + // compare notes (from song-file and from player) + + // move players tone to proper octave + while (CurrentSound.Tone - CurrentLineFragment.Tone > 6) do + CurrentSound.Tone := CurrentSound.Tone - 12; + + while (CurrentSound.Tone - CurrentLineFragment.Tone < -6) do + CurrentSound.Tone := CurrentSound.Tone + 12; + + // half size notes patch + NoteHit := false; + + // if Ini.Difficulty = 0 then Range := 2; + // if Ini.Difficulty = 1 then Range := 1; + // if Ini.Difficulty = 2 then Range := 0; + Range := 2 - Ini.Difficulty; + + // check if the player hit the correct tone within the tolerated range + if (Abs(CurrentLineFragment.Tone - CurrentSound.Tone) <= Range) then + begin + // adjust the players tone to the correct one + // TODO: do we need to do this? + // Philipp: I think we do, at least when we draw the notes. + // Otherwise the notehit thing would be shifted to the + // correct unhit note. I think this will look kind of strange. + CurrentSound.Tone := CurrentLineFragment.Tone; + + // half size notes patch + NoteHit := true; + + if (Ini.LineBonus > 0) then + MaxSongPoints := MAX_SONG_SCORE - MAX_SONG_LINE_BONUS + else + MaxSongPoints := MAX_SONG_SCORE; + + // Note: ScoreValue is the sum of all note values of the song + // (MaxSongPoints / ScoreValue) is the points that a player + // gets for a hit of one beat of a normal note + // CurNotePoints is the amount of points that is meassured + // for a hit of the note per full beat + CurNotePoints := (MaxSongPoints / Lines[0].ScoreValue) * ScoreFactor[CurrentLineFragment.NoteType]; + + case CurrentLineFragment.NoteType of + ntNormal: CurrentPlayer.Score := CurrentPlayer.Score + CurNotePoints; + ntGolden: CurrentPlayer.ScoreGolden := CurrentPlayer.ScoreGolden + CurNotePoints; + end; + + // a problem if we use floor instead of round is that a score of + // 10000 points is only possible if the last digit of the total points + // for golden and normal notes is 0. + // if we use round, the max score is 10000 for most songs + // but a score of 10010 is possible if the last digit of the total + // points for golden and normal notes is 5 + // the best solution is to use round for one of these scores + // and round the other score in the opposite direction + // so we assure that the highest possible score is 10000 in every case. + CurrentPlayer.ScoreInt := round(CurrentPlayer.Score / 10) * 10; + + if (CurrentPlayer.ScoreInt < CurrentPlayer.Score) then + //normal score is floored so we have to ceil golden notes score + CurrentPlayer.ScoreGoldenInt := ceil(CurrentPlayer.ScoreGolden / 10) * 10 + else + //normal score is ceiled so we have to floor golden notes score + CurrentPlayer.ScoreGoldenInt := floor(CurrentPlayer.ScoreGolden / 10) * 10; + + + CurrentPlayer.ScoreTotalInt := CurrentPlayer.ScoreInt + + CurrentPlayer.ScoreGoldenInt + + CurrentPlayer.ScoreLineInt; + end; + + end; // operation + end; // for + + // check if we have to add a new note or extend the note's length + if (SentenceDetected = SentenceMax) then + begin + // we will add a new note + NewNote := true; + + // if previous note (if any) was the same, extend previous note + if ((CurrentPlayer.LengthNote > 0) and + (LastPlayerNote <> nil) and + (LastPlayerNote.Tone = CurrentSound.Tone) and + ((LastPlayerNote.Start + LastPlayerNote.Length) = LyricsState.CurrentBeatD)) then + begin + NewNote := false; + end; + + // if is not as new note to control + for LineFragmentIndex := 0 to Line.HighNote do + begin + if (Line.Note[LineFragmentIndex].Start = LyricsState.CurrentBeatD) then + NewNote := true; + end; + + // add new note + if NewNote then + begin + // new note + Inc(CurrentPlayer.LengthNote); + Inc(CurrentPlayer.HighNote); + SetLength(CurrentPlayer.Note, CurrentPlayer.LengthNote); + + // update player's last note + LastPlayerNote := @CurrentPlayer.Note[CurrentPlayer.HighNote]; + with LastPlayerNote^ do + begin + Start := LyricsState.CurrentBeatD; + Length := 1; + Tone := CurrentSound.Tone; // Tone || ToneAbs + Detect := LyricsState.MidBeat; + Hit := NoteHit; // half note patch + end; + end + else + begin + // extend note length + if (LastPlayerNote <> nil) then + Inc(LastPlayerNote.Length); + end; + + // check for perfect note and then light the star (on Draw) + for LineFragmentIndex := 0 to Line.HighNote do + begin + CurrentLineFragment := @Line.Note[LineFragmentIndex]; + if (CurrentLineFragment.Start = LastPlayerNote.Start) and + (CurrentLineFragment.Length = LastPlayerNote.Length) and + (CurrentLineFragment.Tone = LastPlayerNote.Tone) then + begin + LastPlayerNote.Perfect := true; + end; + end; + end; // if SentenceDetected = SentenceMax + + end; // if Detected + end; // for PlayerIndex + + //Log.LogStatus('EndBeat', 'NewBeat'); + + // on sentence end -> for LineBonus and display of SingBar (rating pop-up) + if (SentenceDetected >= Low(Lines[0].Line)) and + (SentenceDetected <= High(Lines[0].Line)) then + begin + Line := @Lines[0].Line[SentenceDetected]; + CurrentLineFragment := @Line.Note[Line.HighNote]; + if ((CurrentLineFragment.Start + CurrentLineFragment.Length - 1) = LyricsState.CurrentBeatD) then + begin + if assigned(Screen) then + Screen.OnSentenceEnd(SentenceDetected); + end; + end; + +end; + +end. diff --git a/Lua/src/base/UParty.pas b/Lua/src/base/UParty.pas index c9f89efa..e4060e95 100644 --- a/Lua/src/base/UParty.pas +++ b/Lua/src/base/UParty.pas @@ -195,7 +195,7 @@ type function GetTeamRanking: AParty_TeamRanking; { returns a string like "Team 1 (and Team 2) win" } - function GetWinnerString(Round: Integer): String; + function GetWinnerString(Round: integer): UTF8String; destructor Destroy; end; @@ -238,7 +238,7 @@ uses ULuaCore, UDisplay, USong, - UMain, + UNote, SysUtils; //------------- @@ -873,9 +873,9 @@ end; result is name of winners of specified round. if Round is -1 the result is name of winners of the whole party game} -function TPartyGame.GetWinnerString(Round: Integer): String; +function TPartyGame.GetWinnerString(Round: integer): UTF8String; var - Winners: array of String; + Winners: array of UTF8String; I: integer; Ranking: AParty_TeamRanking; begin @@ -901,7 +901,7 @@ begin if (Ranking[I].Rank = PR_First) and (Ranking[I].Team >= 0) and (Ranking[I].Team <= High(Teams)) then begin SetLength(Winners, Length(Winners) + 1); - Winners[high(Winners)] := Teams[Ranking[I].Team].Name; + Winners[high(Winners)] := UTF8String(Teams[Ranking[I].Team].Name); end; end; diff --git a/Lua/src/base/UPath.pas b/Lua/src/base/UPath.pas new file mode 100644 index 00000000..03bd82eb --- /dev/null +++ b/Lua/src/base/UPath.pas @@ -0,0 +1,1413 @@ +{* 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 UPath; + +{$IFDEF FPC} + {$MODE Delphi} +{$ENDIF} + +{$I switches.inc} + +interface + +uses + SysUtils, + Classes, + IniFiles, + {$IFDEF MSWINDOWS} + TntClasses, + {$ENDIF} + UConfig, + UUnicodeUtils; + +type + IPath = interface; + + {** + * TUnicodeMemoryStream + *} + TUnicodeMemoryStream = class(TMemoryStream) + public + procedure LoadFromFile(const FileName: IPath); + procedure SaveToFile(const FileName: IPath); + end; + + {** + * Unicode capable IniFile implementation. + * TMemIniFile and TIniFile are not able to handle INI-files with + * an UTF-8 BOM. This implementation checks if an UTF-8 BOM exists + * and removes it from the internal string-list. + * UTF8Encoded is set accordingly. + *} + TUnicodeMemIniFile = class(TMemIniFile) + private + FFilename: IPath; + FUTF8Encoded: boolean; + public + constructor Create(const FileName: IPath; UTF8Encoded: boolean = false); reintroduce; + procedure UpdateFile; override; + property UTF8Encoded: boolean READ FUTF8Encoded WRITE FUTF8Encoded; + end; + + {** + * TBinaryFileStream (inherited from THandleStream) + *} + {$IFDEF MSWINDOWS} + TBinaryFileStream = class(TTntFileStream) + {$ELSE} + TBinaryFileStream = class(TFileStream) + {$ENDIF} + public + {** + * @seealso TFileStream.Create for valid Mode parameters + *} + constructor Create(const FileName: IPath; Mode: word); + end; + + {** + * TTextFileStream + *} + TTextFileStream = class(TStream) + protected + fLineBreak: RawByteString; + fFilename: IPath; + fMode: word; + + function ReadLine(var Success: boolean): RawByteString; overload; virtual; abstract; + public + constructor Create(Filename: IPath; Mode: word); + + function ReadString(): RawByteString; virtual; abstract; + function ReadLine(var Line: UTF8String): boolean; overload; + function ReadLine(var Line: AnsiString): boolean; overload; + + procedure WriteString(const Str: RawByteString); virtual; + procedure WriteLine(const Line: RawByteString); virtual; + + property LineBreak: RawByteString read fLineBreak write fLineBreak; + property Filename: IPath read fFilename; + end; + + {** + * TMemTextStream + *} + TMemTextFileStream = class(TTextFileStream) + private + fStream: TMemoryStream; + protected + function GetSize: int64; override; + + {** + * Copies fStream.Memory from StartPos to EndPos-1 to the result string; + *} + function CopyMemString(StartPos: int64; EndPos: int64): RawByteString; + public + constructor Create(Filename: IPath; Mode: word); + destructor Destroy(); override; + + function Read(var Buffer; Count: longint): longint; override; + function Write(const Buffer; Count: longint): longint; override; + function Seek(Offset: longint; Origin: word): longint; override; + function Seek(const Offset: int64; Origin: TSeekOrigin): int64; override; + + function ReadLine(var Success: boolean): RawByteString; override; + function ReadString(): RawByteString; override; + end; + + {** + TUnicodeIniStream = class() + end; + *} + + {** + * pdKeep: Keep path as is, neither remove or append a delimiter + * pdAppend: Append a delimiter if path does not have a trailing one + * pdRemove: Remove a trailing delimiter from the path + *} + TPathDelimOption = (pdKeep, pdAppend, pdRemove); + + IPathDynArray = array of IPath; + + {** + * An IPath represents a filename, a directory or a filesystem path in general. + * It hides some of the operating system's specifics like path delimiters + * and encodings and provides an easy to use interface to handle them. + * Internally all paths are stored with the same path delimiter (PathDelim) + * and encoding (UTF-8). The transformation is already done AT THE CREATION of + * the IPath and hence calls to e.g. IPath.Equal() will not distinguish between + * Unix and Windows style paths. + * + * Create new paths with one of the Path() functions. + * If you need a string representation use IPath.ToNative/ToUTF8/ToWide. + * Note that due to the path-delimiter and encoding transformation the string + * might have changed. Path('one\test/path').ToUTF8() might return 'one/test/path'. + * + * It is recommended to use an IPath as long as possible without a string + * conversion (IPath.To...()). The whole Delphi (< 2009) and FPC RTL is ANSI + * only on Windows. If you would use for example FileExists(MyPath.ToNative) + * it would not find a file which contains characters that are not in the + * current locale. Same applies to AssignFile(), TFileStream.Create() and + * everything else in the RTL that expects a filename. + * As a rule of thumb: NEVER use any of the Delphi/FPC RTL filename functions + * if the filename parameter is not of a UTF8String or WideString type. + * + * If you need to open a file use TBinaryStream or TFileStream instead. Many + * of the RTL classes offer a LoadFromStream() method so ANSI Open() methods + * can be workaround. + * + * If there is only a ANSI and no IPath/UTF-8/WideString version and you cannot + * even pass a stream instead of a filename be aware that even if you know that + * a filename is ASCII only, subdirectories in an absolute path might contain + * some non-ASCII characters (for example the user's name) and hence might + * fail (if the characters are not in the current locale). + * It is rare but it happens. + * + * IMPORTANT: + * This interface needs the cwstring unit on Unix (Max OS X / Linux) systems. + * Cwstring functions (WideUpperCase, ...) cannot be used by external threads + * as FPC uses Thread-Local-Storage for the implementation. As a result do not + * call IPath stuff by external threads (e.g. in C callbacks or by SDL-threads). + *} + IPath = interface + ['{686BF103-CE43-4598-B85D-A2C3AF950897}'] + {** + * Returns the path as an UTF8 encoded string. + * If UseNativeDelim is set to true, the native path delimiter ('\' on win32) + * is used. If it is set to false the (more) portable '/' delimiter will used. + *} + function ToUTF8(UseNativeDelim: boolean = true): UTF8String; + + {** + * Returns the path as an UTF-16 encoded string. + * If UseNativeDelim is set to true, the native path delimiter ('\' on win32) + * is used. If it is set to false the delimiter will be '/'. + *} + function ToWide(UseNativeDelim: boolean = true): WideString; + + {** + * Returns the path with the system's native encoding and path delimiter. + * Win32: ANSI (use the UTF-16 version IPath.ToWide() whenever possible) + * Mac: UTF8 + * Unix: UTF8 or ANSI according to LC_CTYPE + *} + function ToNative(): RawByteString; + + {** + * Note: File must be closed with FileClose(Handle) after usage + * @seealso SysUtils.FileOpen() + *} + function Open(Mode: longword): THandle; + + {** @seealso SysUtils.ExtractFileDrive() *} + function GetDrive(): IPath; + + {** @seealso SysUtils.ExtractFilePath() *} + function GetPath(): IPath; + + {** @seealso SysUtils.ExtractFileDir() *} + function GetDir(): IPath; + + {** @seealso SysUtils.ExtractFileName() *} + function GetName(): IPath; + + {** @seealso SysUtils.ExtractFileExtension() *} + function GetExtension(): IPath; + + {** + * Returns a copy of the path with the extension changed to Extension. + * The file itself is not changed, use Rename() for this task. + * @seealso SysUtils.ChangeFileExt() + *} + function SetExtension(const Extension: IPath): IPath; overload; + function SetExtension(const Extension: RawByteString): IPath; overload; + function SetExtension(const Extension: WideString): IPath; overload; + + {** + * Returns the representation of the path relative to Basename. + * Note that the basename must be terminated with a path delimiter + * otherwise the last path component will be ignored. + * @seealso SysUtils.ExtractRelativePath() + *} + function GetRelativePath(const BaseName: IPath): IPath; + + {** @seealso SysUtils.ExpandFileName() *} + function GetAbsolutePath(): IPath; + + {** + * Returns the concatenation of this path with Child. If this path does not + * end with a path delimiter one is inserted in front of the Child path. + * Example: Path('parent').Append(Path('child')) -> Path('parent/child') + *} + function Append(const Child: IPath; DelimOption: TPathDelimOption = pdKeep): IPath; overload; + function Append(const Child: RawByteString; DelimOption: TPathDelimOption = pdKeep): IPath; overload; + function Append(const Child: WideString; DelimOption: TPathDelimOption = pdKeep): IPath; overload; + + {** + * Splits the path into its components. Path delimiters are not removed from + * components. + * Example: C:\test\my\dir -> ['C:\', 'test\', 'my\', 'dir'] + *} + function SplitDirs(): IPathDynArray; + + {** + * Returns the parent directory or PATH_NONE if none exists. + *} + function GetParent(): IPath; + + {** + * Checks if this path is a subdir of or file inside Parent. + * If Direct is true this path must be a direct child. + * Example: C:\test\file is a direct child of C:\test and a child of C:\ + *} + function IsChildOf(const Parent: IPath; Direct: boolean): boolean; + + {** + * Adjusts the case of the path on case senstitive filesystems. + * If the path does not exist or the filesystem is case insensitive + * the original path will be returned. Otherwise a corrected copy. + *} + function AdjustCase(AdjustAllLevels: boolean): IPath; + + {** @seealso SysUtils.IncludeTrailingPathDelimiter() *} + function AppendPathDelim(): IPath; + + {** @seealso SysUtils.ExcludeTrailingPathDelimiter() *} + function RemovePathDelim(): IPath; + + function Exists(): boolean; + function IsFile(): boolean; + function IsDirectory(): boolean; + function IsAbsolute(): boolean; + function GetFileAge(): integer; overload; + function GetFileAge(out FileDateTime: TDateTime): boolean; overload; + function GetAttr(): cardinal; + function SetAttr(Attr: Integer): boolean; + function IsReadOnly(): boolean; + function SetReadOnly(ReadOnly: boolean): boolean; + + {** + * Checks if this path points to nothing, that means the path consists of + * the empty string '' and hence equals PATH_NONE. + * This is a shortcut for IPath.Equals('') or IPath.Equals(PATH_NONE). + * If IsUnset() returns true this path and PATH_NONE are equal but they must + * not be identical as the references might point to different objects. + * + * Example: + * Path('').Equals(PATH_EMPTY) -> true + * Path('') = PATH_EMPTY -> false + *} + function IsUnset(): boolean; + function IsSet(): boolean; + + {** + * Compares this path with Other and returns true if both paths are + * equal. Both paths are expanded and trailing slashes excluded before + * comparison. If IgnoreCase is true, the case will be ignored on + * case-sensitive filesystems. + *} + function Equals(const Other: IPath; IgnoreCase: boolean = false): boolean; overload; + function Equals(const Other: RawByteString; IgnoreCase: boolean = false): boolean; overload; + function Equals(const Other: WideString; IgnoreCase: boolean = false): boolean; overload; + + {** + * Searches for a file in DirList. The Result is nil if the file was + * not found. Use IFileSystem.FileFind() instead if you want to use + * wildcards. + * @seealso SysUtils.FileSearch() + *} + function FileSearch(const DirList: IPath): IPath; + + {** File must be closed with FileClose(Handle) after usage } + function CreateFile(): THandle; + function DeleteFile(): boolean; + function CreateDirectory(Force: boolean = false): boolean; + function DeleteEmptyDir(): boolean; + function Rename(const NewName: IPath): boolean; + function CopyFile(const Target: IPath; FailIfExists: boolean): boolean; + + // TODO: Dirwatch stuff + // AddFileChangeListener(Listener: TFileChangeListener); + + {** + * Internal string representation. For debugging only. + *} + function GetIntern: UTF8String; + property Intern: UTF8String READ GetIntern; + end; + +{** + * Creates a new path with the given pathname. PathName can be either in UTF8 + * or the local encoding. + * Notes: + * - On Apple only UTF8 is supported + * - Same applies to Unix with LC_CTYPE set to UTF8 encoding (default on newer systems) + *} +function Path(const PathName: RawByteString; DelimOption: TPathDelimOption = pdKeep): IPath; overload; + +{** + * Creates a new path with the given UTF-16 pathname. + *} +function Path(const PathName: WideString; DelimOption: TPathDelimOption = pdKeep): IPath; overload; + +{** + * Returns a singleton for Path(''). + *} +function PATH_NONE(): IPath; + +implementation + +uses + RTLConsts, + UTextEncoding, + UFilesystem; + +{* + * Due to a compiler bug in FPC <= 2.2.4 reference counting does not work + * properly with interfaces (see http://bugs.freepascal.org/view.php?id=14019). + * + * There are two (probably more) scenarios causes a program to crash: + * + * 1. Assume we execute Path('fail').GetParent().ToUTF8(). The compiler will + * internally create a temporary variable to hold the result of Path('fail'). + * This temporary var is then passed as Self to GetParent(). Unfortunately FPC + * does already decrement the ref-count of the temporary var at the end of the + * call to Path('fail') and the ref-count drops to zero and the temp object + * is destroyed as FPC erroneously assumes that the temp is not used anymore. + * As a result the Self variable in GetParent() will be invalid, the same + * applies to TPathImpl.fName which reference count dropped to zero when the + * temp was destroyed. Hence GetParent() will likely crash. + * If it does not, ToUTF8() will either return some random string + * (e.g. '' or stupid stuff like 'fhwkjehdk') or crash. + * Either way the result of ToUTF8() is messed up. + * This scenario applies whenever a function (or method) is called that returns + * an interfaced object (e.g. an IPath) and the result is used without storing + * a reference to it in a (temporary) variable first. + * + * Tmp := Path('fail'); Tmp2 := Tmp.GetParent(); Tmp2.ToUTF8(); + * + * will not crash but is very impractical and error-prone. Note that Tmp2 cannot + * be replaced with Tmp (see scenario 2). + * + * 2. Another situation this bug will ruin our lives is when a variable to an + * interfaced object is used at the left and right side of an assignment as in: + * MyPath := MyPath.GetParent() + * + * Although the bug is already fixed in the FPC development version 2.3.1 + * it will take quite some time till the next FPC release (> 2.2.4) in which + * this issue is fixed. + * + * To workaround this bug we use some very simple and stupid kind of garbage + * collection. New IPaths are stored in an IInterfaceList (call it GarbaegeList) + * to artificially increase the ref-count of the newly created object. + * This keeps the object alive when FPC's temporary variable comes to the end + * of its lifetime and the object's ref-count is decremented + * (and is now 1 instead of 0). + * Later on, the object is either garbage or referenced by another variable. + * + * Look at + * MyPath := Path('SomeDir/SubDir').GetParent() + * + * (1) The result of Path('SomeDir/SubDir') is garbage as it is not used anymore. + * (2) The result of GetParent() is referenced by MyPath + * Object (1) has a reference count of 1 (as it is only referenced by the + * GarbageList). Object (2) is referenced twice (MyPath + GarbageList). + * When the reference to (2) is finally stored in MyPath we can safely remove + * (1) and (2) from the GarbageList so (1) will be freed and the ref-count of + * (2) will be decremented to 1. + * + * As we do not know when it is safe to remove an object from the GarbageList + * we assume that there are max. GarbageMaxCount IPath elements created until + * the execution of the expression is performed and a reference to the resulting + * object is assigned to a variable so all temps can be safely deleted. + * + * Worst-case scenarios are recursive calls or calls with large call stacks with + * functions that return an IPath. Also keep in mind that multiple threads might + * be executing such functions at the same time. + * A reasonable count might be a max. of 20.000 elements. With an average length + * of 40 UTF8 chars (maybe 60 byte with class info, pointer etc.) per IPath + * this will consume ~1.2MB. + *} +{$IFDEF FPC} +{$IF FPC_VERSION_INT <= 002002004} // <= 2.2.4 + {$DEFINE HAVE_REFCNTBUG} +{$IFEND} +{$ENDIF} + +{$IFDEF HAVE_REFCNTBUG} +const + // when GarbageList.Count reaches GarbageMaxCount the oldest references in + // GarbageList will be deleted until GarbageList.Count equals GarbageAfterCleanCount. + GarbageMaxCount = 20000; + GarbageAfterCleanCount = GarbageMaxCount-1000; + +var + GarbageList: IInterfaceList; +{$ENDIF} + +type + TPathImpl = class(TInterfacedObject, IPath) + private + fName: UTF8String; //<** internal filename string, always UTF8 with PathDelim + + {** + * Unifies the filename. Path-delimiters are replaced by '/'. + *} + procedure Unify(DelimOption: TPathDelimOption); + + {** + * Returns a copy of fName with path delimiters changed to '/'. + *} + function GetPortableString(): UTF8String; + + procedure AssertRefCount; {$IFDEF HasInline}inline;{$ENDIF} + + public + constructor Create(const Name: UTF8String; DelimOption: TPathDelimOption); + destructor Destroy(); override; + + function ToUTF8(UseNativeDelim: boolean): UTF8String; + function ToWide(UseNativeDelim: boolean): WideString; + function ToNative(): RawByteString; + + function Open(Mode: longword): THandle; + + function GetDrive(): IPath; + function GetPath(): IPath; + function GetDir(): IPath; + function GetName(): IPath; + function GetExtension(): IPath; + + function SetExtension(const Extension: IPath): IPath; overload; + function SetExtension(const Extension: RawByteString): IPath; overload; + function SetExtension(const Extension: WideString): IPath; overload; + + function GetRelativePath(const BaseName: IPath): IPath; + function GetAbsolutePath(): IPath; + function GetParent(): IPath; + function SplitDirs(): IPathDynArray; + + function Append(const Child: IPath; DelimOption: TPathDelimOption): IPath; overload; + function Append(const Child: RawByteString; DelimOption: TPathDelimOption): IPath; overload; + function Append(const Child: WideString; DelimOption: TPathDelimOption): IPath; overload; + + function Equals(const Other: IPath; IgnoreCase: boolean): boolean; overload; + function Equals(const Other: RawByteString; IgnoreCase: boolean): boolean; overload; + function Equals(const Other: WideString; IgnoreCase: boolean): boolean; overload; + + function IsChildOf(const Parent: IPath; Direct: boolean): boolean; + + function AdjustCase(AdjustAllLevels: boolean): IPath; + + function AppendPathDelim(): IPath; + function RemovePathDelim(): IPath; + + function GetFileAge(): integer; overload; + function GetFileAge(out FileDateTime: TDateTime): boolean; overload; + function Exists(): boolean; + function IsFile(): boolean; + function IsDirectory(): boolean; + function IsAbsolute(): boolean; + function GetAttr(): cardinal; + function SetAttr(Attr: Integer): boolean; + function IsReadOnly(): boolean; + function SetReadOnly(ReadOnly: boolean): boolean; + + function IsUnset(): boolean; + function IsSet(): boolean; + + function FileSearch(const DirList: IPath): IPath; + + function CreateFile(): THandle; + function DeleteFile(): boolean; + function CreateDirectory(Force: boolean): boolean; + function DeleteEmptyDir(): boolean; + function Rename(const NewName: IPath): boolean; + function CopyFile(const Target: IPath; FailIfExists: boolean): boolean; + + function GetIntern(): UTF8String; + end; + +function Path(const PathName: RawByteString; DelimOption: TPathDelimOption): IPath; +begin + if (IsUTF8String(PathName)) then + Result := TPathImpl.Create(PathName, DelimOption) + else if (IsNativeUTF8()) then + Result := PATH_NONE + else + Result := TPathImpl.Create(AnsiToUtf8(PathName), DelimOption); +end; + +function Path(const PathName: WideString; DelimOption: TPathDelimOption): IPath; +begin + Result := TPathImpl.Create(UTF8Encode(PathName), DelimOption); +end; + + + +procedure TPathImpl.AssertRefCount; +begin + {$IFDEF HAVE_REFCNTBUG} + if (FRefCount <= 0) then + raise Exception.Create('RefCount error: ' + IntToStr(FRefCount)); + {$ENDIF} +end; + +constructor TPathImpl.Create(const Name: UTF8String; DelimOption: TPathDelimOption); +begin + inherited Create(); + fName := Name; + Unify(DelimOption); + {$IFDEF HAVE_REFCNTBUG} + GarbageList.Lock; + if (GarbageList.Count >= GarbageMaxCount) then + begin + while (GarbageList.Count > GarbageAfterCleanCount) do + GarbageList.Delete(0); + end; + GarbageList.Add(Self); + GarbageList.Unlock; + {$ENDIF} +end; + +destructor TPathImpl.Destroy(); +begin + inherited; +end; + +procedure TPathImpl.Unify(DelimOption: TPathDelimOption); +var + I: integer; +begin + // convert all path delimiters to native ones + for I := 1 to Length(fName) do + begin + if (fName[I] in ['\', '/']) and (fName[I] <> PathDelim) then + fName[I] := PathDelim; + end; + + // Include/ExcludeTrailingPathDelimiter need PathDelim as path delimiter + case DelimOption of + pdAppend: fName := IncludeTrailingPathDelimiter(fName); + pdRemove: fName := ExcludeTrailingPathDelimiter(fName); + end; +end; + +function TPathImpl.GetPortableString(): UTF8String; +var + I: integer; +begin + Result := fName; + if (PathDelim = '/') then + Exit; + + for I := 1 to Length(Result) do + begin + if (Result[I] = PathDelim) then + Result[I] := '/'; + end; +end; + +function TPathImpl.ToUTF8(UseNativeDelim: boolean): UTF8String; +begin + AssertRefCount; + + if (UseNativeDelim) then + Result := fName + else + Result := GetPortableString(); +end; + +function TPathImpl.ToWide(UseNativeDelim: boolean): WideString; +begin + if (UseNativeDelim) then + Result := UTF8Decode(fName) + else + Result := UTF8Decode(GetPortableString()); +end; + +function TPathImpl.ToNative(): RawByteString; +begin + if (IsNativeUTF8()) then + Result := fName + else + Result := Utf8ToAnsi(fName); +end; + +function TPathImpl.GetDrive(): IPath; +begin + AssertRefCount; + Result := FileSystem.ExtractFileDrive(Self); +end; + +function TPathImpl.GetPath(): IPath; +begin + AssertRefCount; + Result := FileSystem.ExtractFilePath(Self); +end; + +function TPathImpl.GetDir(): IPath; +begin + AssertRefCount; + Result := FileSystem.ExtractFileDir(Self); +end; + +function TPathImpl.GetName(): IPath; +begin + AssertRefCount; + Result := FileSystem.ExtractFileName(Self); +end; + +function TPathImpl.GetExtension(): IPath; +begin + AssertRefCount; + Result := FileSystem.ExtractFileExt(Self); +end; + +function TPathImpl.SetExtension(const Extension: IPath): IPath; +begin + AssertRefCount; + Result := FileSystem.ChangeFileExt(Self, Extension); +end; + +function TPathImpl.SetExtension(const Extension: RawByteString): IPath; +begin + Result := SetExtension(Path(Extension)); +end; + +function TPathImpl.SetExtension(const Extension: WideString): IPath; +begin + Result := SetExtension(Path(Extension)); +end; + +function TPathImpl.GetRelativePath(const BaseName: IPath): IPath; +begin + AssertRefCount; + Result := FileSystem.ExtractRelativePath(BaseName, Self); +end; + +function TPathImpl.GetAbsolutePath(): IPath; +begin + AssertRefCount; + Result := FileSystem.ExpandFileName(Self); +end; + +function TPathImpl.GetParent(): IPath; +var + CurPath, ParentPath: IPath; +begin + AssertRefCount; + + Result := PATH_NONE; + + CurPath := Self.RemovePathDelim(); + // check if current path has a parent (no further '/') + if (Pos(PathDelim, CurPath.ToUTF8()) = 0) then + Exit; + + // set new path and check if it has changed to avoid endless loops + // e.g. with invalid paths like '/C:' (GetPath() uses ':' as delimiter too) + // on delphi/win32 + ParentPath := CurPath.GetPath(); + if (ParentPath.ToUTF8 = CurPath.ToUTF8) then + Exit; + + Result := ParentPath; +end; + +function TPathImpl.SplitDirs(): IPathDynArray; +var + CurPath: IPath; + Components: array of IPath; + CurPathStr: UTF8String; + DelimPos: integer; + I: integer; +begin + SetLength(Result, 0); + + if (Length(Self.ToUTF8(true)) = 0) then + Exit; + + CurPath := Self; + SetLength(Components, 0); + repeat + SetLength(Components, Length(Components)+1); + + CurPathStr := CurPath.ToUTF8(); + DelimPos := LastDelimiter(PathDelim, SysUtils.ExcludeTrailingPathDelimiter(CurPathStr)); + Components[High(Components)] := Path(Copy(CurPathStr, DelimPos+1, Length(CurPathStr))); + + CurPath := CurPath.GetParent(); + until (CurPath = PATH_NONE); + + // reverse list + SetLength(Result, Length(Components)); + for I := 0 to High(Components) do + Result[I] := Components[High(Components)-I]; +end; + +function TPathImpl.Append(const Child: IPath; DelimOption: TPathDelimOption): IPath; +var + TmpResult: IPath; +begin + AssertRefCount; + + if (fName = '') then + TmpResult := Child + else + TmpResult := Path(Self.AppendPathDelim().ToUTF8() + Child.ToUTF8()); + + case DelimOption of + pdKeep: Result := TmpResult; + pdAppend: Result := TmpResult.AppendPathDelim; + pdRemove: Result := TmpResult.RemovePathDelim; + end; +end; + +function TPathImpl.Append(const Child: RawByteString; DelimOption: TPathDelimOption): IPath; +begin + AssertRefCount; + Result := Append(Path(Child), DelimOption); +end; + +function TPathImpl.Append(const Child: WideString; DelimOption: TPathDelimOption): IPath; +begin + AssertRefCount; + Result := Append(Path(Child), DelimOption); +end; + +function TPathImpl.Equals(const Other: IPath; IgnoreCase: boolean): boolean; +var + SelfPath, OtherPath: UTF8String; +begin + SelfPath := Self.GetAbsolutePath().RemovePathDelim().ToUTF8(); + OtherPath := Other.GetAbsolutePath().RemovePathDelim().ToUTF8(); + if (FileSystem.IsCaseSensitive() and not IgnoreCase) then + Result := (CompareStr(SelfPath, OtherPath) = 0) + else + Result := (CompareText(SelfPath, OtherPath) = 0); +end; + +function TPathImpl.Equals(const Other: RawByteString; IgnoreCase: boolean): boolean; +begin + Result := Equals(Path(Other), IgnoreCase); +end; + +function TPathImpl.Equals(const Other: WideString; IgnoreCase: boolean): boolean; +begin + Result := Equals(Path(Other), IgnoreCase); +end; + +function TPathImpl.IsChildOf(const Parent: IPath; Direct: boolean): boolean; +var + SelfPath, ParentPath: UTF8String; +begin + Result := false; + + if (Direct) then + begin + SelfPath := Self.GetParent().GetAbsolutePath().AppendPathDelim().ToUTF8(); + ParentPath := Parent.GetAbsolutePath().AppendPathDelim().ToUTF8(); + + // simply check if this paths parent path (SelfPath) equals ParentPath + Result := (SelfPath = ParentPath); + end + else + begin + SelfPath := Self.GetAbsolutePath().AppendPathDelim().ToUTF8(); + ParentPath := Parent.GetAbsolutePath().AppendPathDelim().ToUTF8(); + + if (Length(SelfPath) <= Length(ParentPath)) then + Exit; + + // check if ParentPath is a substring of SelfPath + if (FileSystem.IsCaseSensitive()) then + Result := (StrLComp(PAnsiChar(SelfPath), PAnsiChar(ParentPath), Length(ParentPath)) = 0) + else + Result := (StrLIComp(PAnsiChar(SelfPath), PAnsiChar(ParentPath), Length(ParentPath)) = 0) + end; +end; + +function AdjustCaseRecursive(CurPath: IPath; AdjustAllLevels: boolean): IPath; +var + OldParent, AdjustedParent: IPath; + LocalName: IPath; + PathFound: IPath; + PathWithAdjParent: IPath; + SearchInfo: TFileInfo; + FileIter: IFileIterator; + Pattern: IPath; +begin + // if case-sensitive path exists there is no need to adjust case + if (CurPath.Exists()) then + begin + Result := CurPath; + Exit; + end; + + LocalName := CurPath.RemovePathDelim().GetName(); + + // try to adjust parent + OldParent := CurPath.GetParent(); + if (OldParent <> PATH_NONE) then + begin + if (not AdjustAllLevels) then + begin + AdjustedParent := OldParent; + end + else + begin + AdjustedParent := AdjustCaseRecursive(OldParent, AdjustAllLevels); + if (AdjustedParent = nil) then + begin + // parent path was not found case-insensitive + Result := nil; + Exit; + end; + + // check if the path with adjusted parent can be found now + PathWithAdjParent := AdjustedParent.Append(LocalName); + if (PathWithAdjParent.Exists()) then + begin + Result := PathWithAdjParent; + Exit; + end; + end; + Pattern := AdjustedParent.Append(Path('*')); + end + else // path has no parent + begin + // the top path can either be absolute or relative + if (CurPath.IsAbsolute) then + begin + // the only absolute directory at Unix without a parent is root ('/') + // and hence does not need to be adjusted + Result := CurPath; + Exit; + end; + // this is a relative path, search in the current working dir + AdjustedParent := nil; + Pattern := Path('*'); + end; + + // compare name with all files in the current directory case-insensitive + FileIter := FileSystem.FileFind(Pattern, faAnyFile); + while (FileIter.HasNext()) do + begin + SearchInfo := FileIter.Next(); + PathFound := SearchInfo.Name; + if (CompareText(LocalName.ToUTF8, PathFound.ToUTF8) = 0) then + begin + if (AdjustedParent <> nil) then + Result := AdjustedParent.Append(PathFound) + else + Result := PathFound; + Exit; + end; + end; + + // no matching file found + Result := nil; +end; + +function TPathImpl.AdjustCase(AdjustAllLevels: boolean): IPath; +begin + AssertRefCount; + + Result := Self; + + if (FileSystem.IsCaseSensitive) then + begin + Result := AdjustCaseRecursive(Self, AdjustAllLevels); + if (Result = nil) then + Result := Self; + end; +end; + +function TPathImpl.AppendPathDelim(): IPath; +begin + AssertRefCount; + Result := FileSystem.IncludeTrailingPathDelimiter(Self); +end; + +function TPathImpl.RemovePathDelim(): IPath; +begin + AssertRefCount; + Result := FileSystem.ExcludeTrailingPathDelimiter(Self); +end; + +function TPathImpl.CreateFile(): THandle; +begin + Result := FileSystem.FileCreate(Self); +end; + +function TPathImpl.CreateDirectory(Force: boolean): boolean; +begin + if (Force) then + Result := FileSystem.ForceDirectories(Self) + else + Result := FileSystem.DirectoryCreate(Self); +end; + +function TPathImpl.Open(Mode: longword): THandle; +begin + Result := FileSystem.FileOpen(Self, Mode); +end; + +function TPathImpl.GetFileAge(): integer; +begin + Result := FileSystem.FileAge(Self); +end; + +function TPathImpl.GetFileAge(out FileDateTime: TDateTime): boolean; +begin + Result := FileSystem.FileAge(Self, FileDateTime); +end; + +function TPathImpl.Exists(): boolean; +begin + // note the different specifications of FileExists() on Win32 <> Unix + {$IFDEF MSWINDOWS} + Result := IsFile() or IsDirectory(); + {$ELSE} + Result := FileSystem.FileExists(Self); + {$ENDIF} +end; + +function TPathImpl.IsFile(): boolean; +begin + // note the different specifications of FileExists() on Win32 <> Unix + {$IFDEF MSWINDOWS} + Result := FileSystem.FileExists(Self); + {$ELSE} + Result := Exists() and not IsDirectory(); + {$ENDIF} +end; + +function TPathImpl.IsDirectory(): boolean; +begin + Result := FileSystem.DirectoryExists(Self); +end; + +function TPathImpl.IsAbsolute(): boolean; +begin + AssertRefCount; + Result := FileSystem.FileIsReadOnly(Self); +end; + +function TPathImpl.GetAttr(): cardinal; +begin + Result := FileSystem.FileGetAttr(Self); +end; + +function TPathImpl.SetAttr(Attr: Integer): boolean; +begin + Result := FileSystem.FileSetAttr(Self, Attr); +end; + +function TPathImpl.IsReadOnly(): boolean; +begin + Result := FileSystem.FileIsReadOnly(Self); +end; + +function TPathImpl.SetReadOnly(ReadOnly: boolean): boolean; +begin + Result := FileSystem.FileSetReadOnly(Self, ReadOnly); +end; + +function TPathImpl.IsUnset(): boolean; +begin + Result := (fName = ''); +end; + +function TPathImpl.IsSet(): boolean; +begin + Result := (fName <> ''); +end; + +function TPathImpl.FileSearch(const DirList: IPath): IPath; +begin + AssertRefCount; + Result := FileSystem.FileSearch(Self, DirList); +end; + +function TPathImpl.Rename(const NewName: IPath): boolean; +begin + Result := FileSystem.RenameFile(Self, NewName); +end; + +function TPathImpl.DeleteFile(): boolean; +begin + Result := FileSystem.DeleteFile(Self); +end; + +function TPathImpl.DeleteEmptyDir(): boolean; +begin + Result := FileSystem.RemoveDir(Self); +end; + +function TPathImpl.CopyFile(const Target: IPath; FailIfExists: boolean): boolean; +begin + Result := FileSystem.CopyFile(Self, Target, FailIfExists); +end; + +function TPathImpl.GetIntern(): UTF8String; +begin + Result := fName; +end; + + +{ TBinaryFileStream } + +constructor TBinaryFileStream.Create(const FileName: IPath; Mode: word); +begin +{$IFDEF MSWINDOWS} + inherited Create(FileName.ToWide(), Mode); +{$ELSE} + inherited Create(FileName.ToNative(), Mode); +{$ENDIF} +end; + +{ TTextStream } + +constructor TTextFileStream.Create(Filename: IPath; Mode: word); +begin + inherited Create(); + fMode := Mode; + fFilename := Filename; + fLineBreak := sLineBreak; +end; + +function TTextFileStream.ReadLine(var Line: UTF8String): boolean; +begin + Line := ReadLine(Result); +end; + +function TTextFileStream.ReadLine(var Line: AnsiString): boolean; +begin + Line := ReadLine(Result); +end; + +procedure TTextFileStream.WriteString(const Str: RawByteString); +begin + WriteBuffer(Str[1], Length(Str)); +end; + +procedure TTextFileStream.WriteLine(const Line: RawByteString); +begin + WriteBuffer(Line[1], Length(Line)); + WriteBuffer(fLineBreak[1], Length(fLineBreak)); +end; + +{ TMemTextStream } + +constructor TMemTextFileStream.Create(Filename: IPath; Mode: word); +var + FileStream: TBinaryFileStream; +begin + inherited Create(Filename, Mode); + + fStream := TMemoryStream.Create(); + + // load data to memory in read mode + if ((Mode and 3) in [fmOpenRead, fmOpenReadWrite]) then + begin + FileStream := TBinaryFileStream.Create(Filename, fmOpenRead); + try + fStream.LoadFromStream(FileStream); + finally + FileStream.Free; + end; + end + // check if file exists for write-mode + else if ((Mode and 3) = fmOpenWrite) and (not Filename.IsFile) then + begin + raise EFOpenError.CreateResFmt(@SFOpenError, + [FileName.GetAbsolutePath.ToNative]); + end; +end; + +destructor TMemTextFileStream.Destroy(); +var + FileStream: TBinaryFileStream; + SaveMode: word; +begin + // save changes in write mode (= not read-only mode) + if ((fMode and 3) <> fmOpenRead) then + begin + if (fMode = fmCreate) then + SaveMode := fmCreate + else + SaveMode := fmOpenWrite; + FileStream := TBinaryFileStream.Create(fFilename, SaveMode); + try + fStream.SaveToStream(FileStream); + finally + FileStream.Free; + end; + end; + + fStream.Free; + inherited; +end; + +function TMemTextFileStream.GetSize: int64; +begin + Result := fStream.Size; +end; + +function TMemTextFileStream.Read(var Buffer; Count: longint): longint; +begin + Result := fStream.Read(Buffer, Count); +end; + +function TMemTextFileStream.Write(const Buffer; Count: longint): longint; +begin + Result := fStream.Write(Buffer, Count); +end; + +function TMemTextFileStream.Seek(Offset: longint; Origin: word): longint; +begin + Result := fStream.Seek(Offset, Origin); +end; + +function TMemTextFileStream.Seek(const Offset: int64; Origin: TSeekOrigin): int64; +begin + Result := fStream.Seek(Offset, Origin); +end; + +function TMemTextFileStream.CopyMemString(StartPos: int64; EndPos: int64): RawByteString; +var + LineLength: cardinal; + Temp: RawByteString; +begin + LineLength := EndPos - StartPos; + if (LineLength > 0) then + begin + // set string length to line-length (+ zero-terminator) + SetLength(Temp, LineLength); + StrLCopy(PAnsiChar(Temp), + @PAnsiChar(fStream.Memory)[StartPos], + LineLength); + Result := Temp; + end + else + begin + Result := ''; + end; +end; + +function TMemTextFileStream.ReadString(): RawByteString; +var + TextPtr: PAnsiChar; + CurPos, StartPos, FileSize: int64; +begin + TextPtr := PAnsiChar(fStream.Memory); + CurPos := Position; + FileSize := Size; + StartPos := -1; + + while (CurPos < FileSize) do + begin + // check for whitespace (tab, lf, cr, space) + if (TextPtr[CurPos] in [#9, #10, #13, ' ']) then + begin + // check if we are at the end of a string + if (StartPos > -1) then + Break; + end + else if (StartPos = -1) then // start of string found + begin + StartPos := CurPos; + end; + Inc(CurPos); + end; + + if (StartPos = -1) then + Result := '' + else + begin + Result := CopyMemString(StartPos, CurPos); + fStream.Position := CurPos; + end; +end; + +{* + * Implementation of ReadLine(). We need separate versions for UTF8String + * and AnsiString as "var" parameter types have to fit exactly. + * To avoid a var-parameter here, the internal version the Line parameter is + * used as return value. + *} +function TMemTextFileStream.ReadLine(var Success: boolean): RawByteString; +var + TextPtr: PAnsiChar; + CurPos, FileSize: int64; +begin + TextPtr := PAnsiChar(fStream.Memory); + CurPos := fStream.Position; + FileSize := Size; + + // check for EOF + if (CurPos >= FileSize) then + begin + Result := ''; + Success := false; + Exit; + end; + + Success := true; + + while (CurPos < FileSize) do + begin + if (TextPtr[CurPos] in [#10, #13]) then + begin + // copy text line + Result := CopyMemString(fStream.Position, CurPos); + + // handle windows style #13#10 (\r\n) newlines + if (TextPtr[CurPos] = #13) and + (CurPos+1 < FileSize) and + (TextPtr[CurPos+1] = #10) then + begin + Inc(CurPos); + end; + + // update stream pos + fStream.Position := CurPos+1; + + Exit; + end; + Inc(CurPos); + end; + + Result := CopyMemString(fStream.Position, CurPos); + fStream.Position := FileSize; +end; + +{ TUnicodeMemoryStream } + +procedure TUnicodeMemoryStream.LoadFromFile(const FileName: IPath); +var + Stream: TStream; +begin + Stream := TBinaryFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite); + try + LoadFromStream(Stream); + finally + Stream.Free; + end; +end; + +procedure TUnicodeMemoryStream.SaveToFile(const FileName: IPath); +var + Stream: TStream; +begin + Stream := TBinaryFileStream.Create(FileName, fmCreate); + try + SaveToStream(Stream); + finally + Stream.Free; + end; +end; + +{ TUnicodeMemIniFile } + +constructor TUnicodeMemIniFile.Create(const FileName: IPath; UTF8Encoded: boolean); +var + List: TStringList; + Stream: TBinaryFileStream; + BOMBuf: array[0..2] of AnsiChar; +begin + inherited Create(''); + FFilename := FileName; + FUTF8Encoded := UTF8Encoded; + + if FileName.Exists() then + begin + List := nil; + Stream := nil; + try + List := TStringList.Create; + Stream := TBinaryFileStream.Create(FileName, fmOpenRead); + if (Stream.Read(BOMBuf[0], SizeOf(BOMBuf)) = 3) and + (CompareMem(PChar(UTF8_BOM), @BomBuf, Length(UTF8_BOM))) then + begin + // truncate BOM + FUTF8Encoded := true; + end + else + begin + // rewind file + Stream.Seek(0, soBeginning); + end; + List.LoadFromStream(Stream); + SetStrings(List); + finally + Stream.Free; + List.Free; + end; + end; +end; + +procedure TUnicodeMemIniFile.UpdateFile; +var + List: TStringList; + Stream: TBinaryFileStream; +begin + List := nil; + Stream := nil; + try + List := TStringList.Create; + GetStrings(List); + Stream := TBinaryFileStream.Create(FFileName, fmCreate); + if UTF8Encoded then + Stream.Write(UTF8_BOM, Length(UTF8_BOM)); + List.SaveToStream(Stream); + finally + List.Free; + Stream.Free; + end; +end; + + +var + PATH_NONE_Singelton: IPath; + +function PATH_NONE(): IPath; +begin + Result := PATH_NONE_Singelton; +end; + +initialization + {$IFDEF HAVE_REFCNTBUG} + GarbageList := TInterfaceList.Create(); + GarbageList.Capacity := GarbageMaxCount; + {$ENDIF} + PATH_NONE_Singelton := Path(''); + +finalization + PATH_NONE_Singelton := nil; + +end. diff --git a/Lua/src/base/UPathUtils.pas b/Lua/src/base/UPathUtils.pas new file mode 100644 index 00000000..c2bcdd4b --- /dev/null +++ b/Lua/src/base/UPathUtils.pas @@ -0,0 +1,196 @@ +{* 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 UPathUtils; + +interface + +{$IFDEF FPC} + {$MODE Delphi} +{$ENDIF} + +{$I switches.inc} + +uses + SysUtils, + Classes, + UPath; + +var + // Absolute Paths + GamePath: IPath; + SoundPath: IPath; + SongPaths: IInterfaceList; + LogPath: IPath; + ThemePath: IPath; + SkinsPath: IPath; + ScreenshotsPath: IPath; + CoverPaths: IInterfaceList; + LanguagesPath: IPath; + PluginPath: IPath; + VisualsPath: IPath; + FontPath: IPath; + ResourcesPath: IPath; + PlaylistPath: IPath; + +function FindPath(out PathResult: IPath; const RequestedPath: IPath; NeedsWritePermission: boolean): boolean; +procedure InitializePaths; +procedure AddSongPath(const Path: IPath); + +implementation + +uses + StrUtils, + UPlatform, + UCommandLine, + ULog; + +procedure AddSpecialPath(var PathList: IInterfaceList; const Path: IPath); +var + Index: integer; + PathAbs, PathTmp: IPath; + OldPath, OldPathAbs, OldPathTmp: IPath; +begin + if (PathList = nil) then + PathList := TInterfaceList.Create; + + if Path.Equals(PATH_NONE) or not Path.CreateDirectory(true) then + Exit; + + PathTmp := Path.GetAbsolutePath(); + PathAbs := PathTmp.AppendPathDelim(); + + // check if path or a part of the path was already added + for Index := 0 to PathList.Count-1 do + begin + OldPath := PathList[Index] as IPath; + OldPathTmp := OldPath.GetAbsolutePath(); + OldPathAbs := OldPathTmp.AppendPathDelim(); + + // check if the new directory is a sub-directory of a previously added one. + // This is also true, if both paths point to the same directories. + if (OldPathAbs.IsChildOf(PathAbs, false) or OldPathAbs.Equals(PathAbs)) then + begin + // ignore the new path + Exit; + end; + + // check if a previously added directory is a sub-directory of the new one. + if (PathAbs.IsChildOf(OldPathAbs, false)) then + begin + // replace the old with the new one. + PathList[Index] := PathAbs; + Exit; + end; + end; + + PathList.Add(PathAbs); +end; + +procedure AddSongPath(const Path: IPath); +begin + AddSpecialPath(SongPaths, Path); +end; + +procedure AddCoverPath(const Path: IPath); +begin + AddSpecialPath(CoverPaths, Path); +end; + +(** + * Initialize a path variable + * After setting paths, make sure that paths exist + *) +function FindPath( + out PathResult: IPath; + const RequestedPath: IPath; + NeedsWritePermission: boolean): boolean; +begin + Result := false; + + if (RequestedPath.Equals(PATH_NONE)) then + Exit; + + // Make sure the directory exists + if (not RequestedPath.CreateDirectory(true)) then + begin + PathResult := PATH_NONE; + Exit; + end; + + PathResult := RequestedPath.AppendPathDelim(); + + if (NeedsWritePermission) and RequestedPath.IsReadOnly() then + Exit; + + Result := true; +end; + +(** + * Function sets all absolute paths e.g. song path and makes sure the directorys exist + *) +procedure InitializePaths; +var + SharedPath, UserPath: IPath; +begin + // Log directory (must be writable) + if (not FindPath(LogPath, Platform.GetLogPath, true)) then + begin + Log.FileOutputEnabled := false; + Log.LogWarn('Log directory "'+ Platform.GetLogPath.ToNative +'" not available', 'InitializePaths'); + end; + + SharedPath := Platform.GetGameSharedPath; + UserPath := Platform.GetGameUserPath; + + FindPath(SoundPath, SharedPath.Append('sounds'), false); + FindPath(ThemePath, SharedPath.Append('themes'), false); + FindPath(SkinsPath, SharedPath.Append('themes'), false); + FindPath(LanguagesPath, SharedPath.Append('languages'), false); + FindPath(PluginPath, SharedPath.Append('plugins'), false); + FindPath(VisualsPath, SharedPath.Append('visuals'), false); + FindPath(FontPath, SharedPath.Append('fonts'), false); + FindPath(ResourcesPath, SharedPath.Append('resources'), false); + + // Playlists are not shared as we need one directory to write too + FindPath(PlaylistPath, UserPath.Append('playlists'), true); + + // Screenshot directory (must be writable) + if (not FindPath(ScreenshotsPath, UserPath.Append('screenshots'), true)) then + begin + Log.LogWarn('Screenshot directory "'+ UserPath.ToNative +'" not available', 'InitializePaths'); + end; + + // Add song paths + AddSongPath(Params.SongPath); + AddSongPath(SharedPath.Append('songs')); + AddSongPath(UserPath.Append('songs')); + + // Add category cover paths + AddCoverPath(SharedPath.Append('covers')); + AddCoverPath(UserPath.Append('covers')); +end; + +end. diff --git a/Lua/src/base/UPlatform.pas b/Lua/src/base/UPlatform.pas index e4cb6f0c..11c67fa7 100644 --- a/Lua/src/base/UPlatform.pas +++ b/Lua/src/base/UPlatform.pas @@ -39,28 +39,20 @@ interface {$I switches.inc} uses - Classes; + Classes, + UPath; type - TDirectoryEntry = record - Name : WideString; - IsDirectory : boolean; - IsFile : boolean; - end; - - TDirectoryEntryArray = array of TDirectoryEntry; - TPlatform = class - function GetExecutionDir(): string; + function GetExecutionDir(): IPath; procedure Init; virtual; - function DirectoryFindFiles(Dir, Filter: WideString; ReturnAllSubDirs: boolean): TDirectoryEntryArray; virtual; abstract; - function TerminateIfAlreadyRunning(var WndTitle : string): boolean; virtual; - function FindSongFile(Dir, Mask: WideString): WideString; virtual; + + function TerminateIfAlreadyRunning(var WndTitle: string): boolean; virtual; procedure Halt; virtual; - function GetLogPath : WideString; virtual; abstract; - function GetGameSharedPath : WideString; virtual; abstract; - function GetGameUserPath : WideString; virtual; abstract; - function CopyFile(const Source, Target: WideString; FailIfExists: boolean): boolean; virtual; + + function GetLogPath: IPath; virtual; abstract; + function GetGameSharedPath: IPath; virtual; abstract; + function GetGameUserPath: IPath; virtual; abstract; end; function Platform(): TPlatform; @@ -76,16 +68,18 @@ uses {$ELSEIF Defined(UNIX)} UPlatformLinux, {$IFEND} - ULog; + ULog, + UUnicodeUtils, + UFilesystem; -// I have modified it to use the Platform_singleton in this location ( in the implementaiton ) +// I modified it to use the Platform_singleton in this location (in the implementation) // so that this variable can NOT be overwritten from anywhere else in the application. // the accessor function platform, emulates all previous calls to work the same way. var - Platform_singleton : TPlatform; + Platform_singleton: TPlatform; -function Platform : TPlatform; +function Platform: TPlatform; begin Result := Platform_singleton; end; @@ -109,78 +103,23 @@ end; {** * Returns the directory of the executable *} -function TPlatform.GetExecutionDir(): string; +function TPlatform.GetExecutionDir(): IPath; +var + ExecName, ExecDir: IPath; begin - Result := ExpandFileName(ExtractFilePath(ParamStr(0))); + ExecName := Path(ParamStr(0)); + ExecDir := ExecName.GetPath; + Result := ExecDir.GetAbsolutePath(); end; (** * Default TerminateIfAlreadyRunning() implementation *) -function TPlatform.TerminateIfAlreadyRunning(var WndTitle : string): Boolean; +function TPlatform.TerminateIfAlreadyRunning(var WndTitle: string): boolean; begin Result := false; end; -(** - * Default FindSongFile() implementation - *) -function TPlatform.FindSongFile(Dir, Mask: WideString): WideString; -var - SR: TSearchRec; // for parsing song directory -begin - Result := ''; - if SysUtils.FindFirst(Dir + Mask, faDirectory, SR) = 0 then - begin - Result := SR.Name; - end; - SysUtils.FindClose(SR); -end; - -function TPlatform.CopyFile(const Source, Target: WideString; FailIfExists: boolean): boolean; -const - COPY_BUFFER_SIZE = 4096; // a good tradeoff between speed and memory consumption -var - SourceFile, TargetFile: TFileStream; - FileCopyBuffer: array [0..COPY_BUFFER_SIZE-1] of byte; // temporary copy-buffer. - NumberOfBytes: integer; // number of bytes read from SourceFile -begin - Result := false; - SourceFile := nil; - TargetFile := nil; - - // if overwrite is disabled return if the target file already exists - if (FailIfExists and FileExists(Target)) then - Exit; - - try - try - // open source and target file (might throw an exception on error) - SourceFile := TFileStream.Create(Source, fmOpenRead); - TargetFile := TFileStream.Create(Target, fmCreate or fmOpenWrite); - - while true do - begin - // read a block from the source file and check for errors or EOF - NumberOfBytes := SourceFile.Read(FileCopyBuffer, SizeOf(FileCopyBuffer)); - if (NumberOfBytes <= 0) then - Break; - // write block to target file and check if everything was written - if (TargetFile.Write(FileCopyBuffer, NumberOfBytes) <> NumberOfBytes) then - Exit; - end; - except - Exit; - end; - finally - SourceFile.Free; - TargetFile.Free; - end; - - Result := true; -end; - - initialization {$IF Defined(MSWINDOWS)} Platform_singleton := TPlatformWindows.Create; diff --git a/Lua/src/base/UPlatformLinux.pas b/Lua/src/base/UPlatformLinux.pas index 30499a97..693facaa 100644 --- a/Lua/src/base/UPlatformLinux.pas +++ b/Lua/src/base/UPlatformLinux.pas @@ -36,7 +36,8 @@ interface uses Classes, UPlatform, - UConfig; + UConfig, + UPath; type TPlatformLinux = class(TPlatform) @@ -44,15 +45,13 @@ type UseLocalDirs: boolean; procedure DetectLocalExecution(); - function GetHomeDir(): string; + function GetHomeDir(): IPath; public procedure Init; override; - - function DirectoryFindFiles(Dir, Filter: WideString; ReturnAllSubDirs: Boolean): TDirectoryEntryArray; override; - - function GetLogPath : WideString; override; - function GetGameSharedPath : WideString; override; - function GetGameUserPath : WideString; override; + + function GetLogPath : IPath; override; + function GetGameSharedPath : IPath; override; + function GetGameUserPath : IPath; override; end; implementation @@ -60,9 +59,7 @@ implementation uses UCommandLine, BaseUnix, - {$IF FPC_VERSION_INT >= 2002002} pwd, - {$IFEND} SysUtils, ULog; @@ -88,114 +85,65 @@ end; *} procedure TPlatformLinux.DetectLocalExecution(); var - LocalDir: string; + LocalDir, LanguageDir: IPath; begin - LocalDir := GetExecutionDir(); - // we just check if the 'languages' folder exists in the // directory of the executable. If so -> local execution. - UseLocalDirs := (DirectoryExists(LocalDir + 'languages')); -end; - -function TPlatformLinux.DirectoryFindFiles(Dir, Filter: WideString; ReturnAllSubDirs: Boolean): TDirectoryEntryArray; -var - i: Integer; - TheDir : pDir; - ADirent : pDirent; - Entry : Longint; - lAttrib : integer; -begin - i := 0; - Filter := LowerCase(Filter); - - TheDir := FpOpenDir( Dir ); - if Assigned(TheDir) then - begin - repeat - ADirent := FpReadDir(TheDir^); - - if Assigned(ADirent) and (ADirent^.d_name <> '.') and (ADirent^.d_name <> '..') then - begin - lAttrib := FileGetAttr(Dir + ADirent^.d_name); - if ReturnAllSubDirs and ((lAttrib and faDirectory) <> 0) then - begin - SetLength( Result, i + 1); - Result[i].Name := ADirent^.d_name; - Result[i].IsDirectory := true; - Result[i].IsFile := false; - i := i + 1; - end - else if (Length(Filter) = 0) or (Pos( Filter, LowerCase(ADirent^.d_name)) > 0) then - begin - SetLength( Result, i + 1); - Result[i].Name := ADirent^.d_name; - Result[i].IsDirectory := false; - Result[i].IsFile := true; - i := i + 1; - end; - end; - until (ADirent = nil); - - FpCloseDir(TheDir^); - end; + LocalDir := GetExecutionDir(); + LanguageDir := LocalDir.Append('languages'); + UseLocalDirs := LanguageDir.IsDirectory; end; -function TPlatformLinux.GetLogPath: WideString; +function TPlatformLinux.GetLogPath: IPath; begin if UseLocalDirs then Result := GetExecutionDir() else - Result := GetGameUserPath() + 'logs/'; + Result := GetGameUserPath().Append('logs', pdAppend); // create non-existing directories - ForceDirectories(Result); + Result.CreateDirectory(true); end; -function TPlatformLinux.GetGameSharedPath: WideString; +function TPlatformLinux.GetGameSharedPath: IPath; begin if UseLocalDirs then Result := GetExecutionDir() else - Result := IncludeTrailingPathDelimiter(INSTALL_DATADIR); + Result := Path(INSTALL_DATADIR, pdAppend); end; -function TPlatformLinux.GetGameUserPath: WideString; +function TPlatformLinux.GetGameUserPath: IPath; begin if UseLocalDirs then Result := GetExecutionDir() else - Result := GetHomeDir() + '.ultrastardx/'; + Result := GetHomeDir().Append('.ultrastardx', pdAppend); end; {** * Returns the user's home directory terminated by a path delimiter *} -function TPlatformLinux.GetHomeDir(): string; -{$IF FPC_VERSION_INT >= 2002002} +function TPlatformLinux.GetHomeDir(): IPath; var PasswdEntry: PPasswd; -{$IFEND} begin - Result := ''; + Result := PATH_NONE; - {$IF FPC_VERSION_INT >= 2002002} // try to retrieve the info from passwd PasswdEntry := FpGetpwuid(FpGetuid()); if (PasswdEntry <> nil) then - Result := PasswdEntry.pw_dir; - {$IFEND} + Result := Path(PasswdEntry.pw_dir); // fallback if passwd does not contain the path - if (Result = '') then - Result := GetEnvironmentVariable('HOME'); + if (Result.IsUnset) then + Result := Path(GetEnvironmentVariable('HOME')); // add trailing path delimiter (normally '/') - if (Result <> '') then - Result := IncludeTrailingPathDelimiter(Result); + if (Result.IsSet) then + Result := Result.AppendPathDelim(); - {$IF FPC_VERSION_INT >= 2002002} // GetUserDir() is another function that returns a user path. // It uses env-var HOME or a fallback to a temp-dir. //Result := GetUserDir(); - {$IFEND} end; end. diff --git a/Lua/src/base/UPlatformMacOSX.pas b/Lua/src/base/UPlatformMacOSX.pas index 9085e337..1dc0014a 100644 --- a/Lua/src/base/UPlatformMacOSX.pas +++ b/Lua/src/base/UPlatformMacOSX.pas @@ -36,7 +36,9 @@ interface uses Classes, ULog, - UPlatform; + UPlatform, + UFilesystem, + UPath; type {** @@ -70,19 +72,19 @@ type * * So * GetGameSharedPath could return - * /Library/Application Support/UltraStarDeluxe/Resources/. + * /Library/Application Support/UltraStarDeluxe/. * GetGameUserPath could return - * ~/Library/Application Support/UltraStarDeluxe/Resources/. + * ~/Library/Application Support/UltraStarDeluxe/. * - * Right now, only $HOME/Library/Application Support/UltraStarDeluxe/Resources + * Right now, only $HOME/Library/Application Support/UltraStarDeluxe * is used. So every user needs the complete set of files and folders. * Future versions may also use shared resources in - * /Library/Application Support/UltraStarDeluxe/Resources. However, this is + * /Library/Application Support/UltraStarDeluxe. However, this is * not treated yet in the code outside this unit. * * USDX checks, whether GetGameUserPath exists. If not, USDX creates it. * The existence of needed files is then checked and if a file is missing - * it is copied to there from within the Resources folder in the Application + * it is copied to there from within the folder Contents in the Application * bundle, which contains the default files. USDX should not delete files or * folders in Application Support/UltraStarDeluxe automatically or without * user confirmation. @@ -93,60 +95,55 @@ type * GetBundlePath returns the path to the application bundle * UltraStarDeluxe.app. *} - function GetBundlePath: WideString; + function GetBundlePath: IPath; {** * GetApplicationSupportPath returns the path to - * $HOME/Library/Application Support/UltraStarDeluxe/Resources. + * $HOME/Library/Application Support/UltraStarDeluxe. *} - function GetApplicationSupportPath: WideString; + function GetApplicationSupportPath: IPath; {** * see the description of @link(Init). *} procedure CreateUserFolders(); + function GetHomeDir(): IPath; + public {** * Init simply calls @link(CreateUserFolders), which in turn scans the - * folder UltraStarDeluxe.app/Contents/Resources for all files and - * folders. $HOME/Library/Application Support/UltraStarDeluxe/Resources + * folder UltraStarDeluxe.app/Contents for all files and + * folders. $HOME/Library/Application Support/UltraStarDeluxe * is then checked for their presence and missing ones are copied. *} procedure Init; override; {** - * DirectoryFindFiles returns all entries of a folder with names and - * booleans about their type, i.e. file or directory. - *} - function DirectoryFindFiles(Dir, Filter: WideString; ReturnAllSubDirs: boolean): TDirectoryEntryArray; override; - - {** * GetLogPath returns the path for log messages. Currently it is set to - * $HOME/Library/Application Support/UltraStarDeluxe/Resources/Log. + * $HOME/Library/Application Support/UltraStarDeluxe/Log. *} - function GetLogPath : WideString; override; + function GetLogPath : IPath; override; {** * GetGameSharedPath returns the path for shared resources. Currently it - * is set to /Library/Application Support/UltraStarDeluxe/Resources. + * is set to /Library/Application Support/UltraStarDeluxe. * However it is not used. *} - function GetGameSharedPath : WideString; override; + function GetGameSharedPath : IPath; override; {** * GetGameUserPath returns the path for user resources. Currently it is - * set to $HOME/Library/Application Support/UltraStarDeluxe/Resources. + * set to $HOME/Library/Application Support/UltraStarDeluxe. * This is where a user can add songs, themes, .... *} - function GetGameUserPath : WideString; override; + function GetGameUserPath : IPath; override; end; implementation uses - SysUtils, - BaseUnix; + SysUtils; procedure TPlatformMacOSX.Init; begin @@ -154,178 +151,129 @@ begin end; procedure TPlatformMacOSX.CreateUserFolders(); -const - // used to construct the @link(UserPathName) - PathName: string = '/Library/Application Support/UltraStarDeluxe/Resources'; var - RelativePath: string; + RelativePath: IPath; // BaseDir contains the path to the folder, where a search is performed. // It is set to the entries in @link(DirectoryList) one after the other. - BaseDir: string; + BaseDir: IPath; // OldBaseDir contains the path to the folder, where the search started. // It is used to return to it, when the search is completed in all folders. - OldBaseDir: string; - // This record contains the result of a file search with FindFirst or FindNext - SearchInfo: TSearchRec; + OldBaseDir: IPath; + Iter: IFileIterator; + FileInfo: TFileInfo; + CurPath: IPath; // These two lists contain all folder and file names found // within the folder @link(BaseDir). - DirectoryList, FileList: TStringList; + DirectoryList, FileList: IInterfaceList; // DirectoryIsFinished contains the index of the folder in @link(DirectoryList), // which is the last one completely searched. Later folders are still to be // searched for additional files and folders. DirectoryIsFinished: longint; - Counter: longint; + I: longint; // These three are for creating directories, due to possible symlinks CreatedDirectory: boolean; FileAttrs: integer; - DirectoryPath: string; - - UserPathName: string; + DirectoryPath: IPath; + UserPath: IPath; + SrcFile, TgtFile: IPath; begin // Get the current folder and save it in OldBaseDir for returning to it, when // finished. - GetDir(0, OldBaseDir); + OldBaseDir := FileSystem.GetCurrentDir(); - // UltraStarDeluxe.app/Contents/Resources contains all the default files and - // folders. - BaseDir := OldBaseDir + '/UltraStarDeluxe.app/Contents/Resources'; - ChDir(BaseDir); + // UltraStarDeluxe.app/Contents contains all the default files and folders. + BaseDir := OldBaseDir.Append('UltraStarDeluxe.app/Contents'); + FileSystem.SetCurrentDir(BaseDir); - // Right now, only $HOME/Library/Application Support/UltraStarDeluxe/Resources - // is used. - UserPathName := GetEnvironmentVariable('HOME') + PathName; + // Right now, only $HOME/Library/Application Support/UltraStarDeluxe is used. + UserPath := GetGameUserPath(); DirectoryIsFinished := 0; - DirectoryList := TStringList.Create(); - FileList := TStringList.Create(); - DirectoryList.Add('.'); + // replace with IInterfaceList + DirectoryList := TInterfaceList.Create(); + FileList := TInterfaceList.Create(); + DirectoryList.Add(Path('.')); // create the folder and file lists repeat - - RelativePath := DirectoryList[DirectoryIsFinished]; - ChDir(BaseDir + '/' + RelativePath); - if (FindFirst('*', faAnyFile, SearchInfo) = 0) then + RelativePath := (DirectoryList[DirectoryIsFinished] as IPath); + FileSystem.SetCurrentDir(BaseDir.Append(RelativePath)); + Iter := FileSystem.FileFind(Path('*'), faAnyFile); + while (Iter.HasNext) do begin - repeat - if DirectoryExists(SearchInfo.Name) then - begin - if (SearchInfo.Name <> '.') and (SearchInfo.Name <> '..') then - DirectoryList.Add(RelativePath + '/' + SearchInfo.Name); - end - else - Filelist.Add(RelativePath + '/' + SearchInfo.Name); - until (FindNext(SearchInfo) <> 0); + FileInfo := Iter.Next; + CurPath := FileInfo.Name; + if CurPath.IsDirectory() then + begin + if (not CurPath.Equals('.')) and (not CurPath.Equals('..')) then + DirectoryList.Add(RelativePath.Append(CurPath)); + end + else + Filelist.Add(RelativePath.Append(CurPath)); end; - FindClose(SearchInfo); Inc(DirectoryIsFinished); until (DirectoryIsFinished = DirectoryList.Count); // create missing folders - ForceDirectories(UserPathName); // should not be necessary since (UserPathName+'/.') is created. - for Counter := 0 to DirectoryList.Count-1 do + UserPath.CreateDirectory(true); // should not be necessary since (UserPathName+'/.') is created. + for I := 0 to DirectoryList.Count-1 do begin - DirectoryPath := UserPathName + '/' + DirectoryList[Counter]; - CreatedDirectory := ForceDirectories(DirectoryPath); - FileAttrs := FileGetAttr(DirectoryPath); - // Don't know how to analyse the target of the link. + CurPath := DirectoryList[I] as IPath; + DirectoryPath := UserPath.Append(CurPath); + CreatedDirectory := DirectoryPath.CreateDirectory(); + FileAttrs := DirectoryPath.GetAttr(); + // Maybe analyse the target of the link with FpReadlink(). // Let's assume the symlink is pointing to an existing directory. if (not CreatedDirectory) and (FileAttrs and faSymLink > 0) then - Log.LogError('Failed to create the folder "'+ UserPathName + '/' + DirectoryList[Counter] +'"', + Log.LogError('Failed to create the folder "'+ DirectoryPath.ToNative +'"', 'TPlatformMacOSX.CreateUserFolders'); end; - DirectoryList.Free(); // copy missing files - for Counter := 0 to Filelist.Count-1 do + for I := 0 to Filelist.Count-1 do begin - CopyFile(BaseDir + '/' + Filelist[Counter], - UserPathName + '/' + Filelist[Counter], true); + CurPath := Filelist[I] as IPath; + SrcFile := BaseDir.Append(CurPath); + TgtFile := UserPath.Append(CurPath); + SrcFile.CopyFile(TgtFile, true); end; - FileList.Free(); // go back to the initial folder - ChDir(OldBaseDir); + FileSystem.SetCurrentDir(OldBaseDir); end; -function TPlatformMacOSX.GetBundlePath: WideString; -var - i, pos : integer; +function TPlatformMacOSX.GetBundlePath: IPath; begin // Mac applications are packaged in folders. // Cutting the last two folders yields the application folder. - - Result := GetExecutionDir(); - for i := 1 to 2 do - begin - pos := Length(Result); - repeat - Delete(Result, pos, 1); - pos := Length(Result); - until (pos = 0) or (Result[pos] = '/'); - end; + Result := GetExecutionDir().GetParent().GetParent(); end; -function TPlatformMacOSX.GetApplicationSupportPath: WideString; +function TPlatformMacOSX.GetApplicationSupportPath: IPath; const - PathName : string = '/Library/Application Support/UltraStarDeluxe/Resources'; + PathName: string = 'Library/Application Support/UltraStarDeluxe'; begin - Result := GetEnvironmentVariable('HOME') + PathName + '/'; + Result := GetHomeDir().Append(PathName, pdAppend); end; -function TPlatformMacOSX.GetLogPath: WideString; +function TPlatformMacOSX.GetHomeDir(): IPath; begin - Result := GetApplicationSupportPath + 'Logs'; + Result := Path(GetEnvironmentVariable('HOME')); end; -function TPlatformMacOSX.GetGameSharedPath: WideString; +function TPlatformMacOSX.GetLogPath: IPath; begin - Result := GetApplicationSupportPath; + Result := GetApplicationSupportPath.Append('Logs'); end; -function TPlatformMacOSX.GetGameUserPath: WideString; +function TPlatformMacOSX.GetGameSharedPath: IPath; begin Result := GetApplicationSupportPath; end; -function TPlatformMacOSX.DirectoryFindFiles(Dir, Filter: WideString; ReturnAllSubDirs: boolean): TDirectoryEntryArray; -var - i : integer; - TheDir : pdir; - ADirent : pDirent; - lAttrib : integer; +function TPlatformMacOSX.GetGameUserPath: IPath; begin - i := 0; - Filter := LowerCase(Filter); - - TheDir := FPOpenDir(Dir); - if Assigned(TheDir) then - repeat - ADirent := FPReadDir(TheDir); - - if Assigned(ADirent) and (ADirent^.d_name <> '.') and (ADirent^.d_name <> '..') then - begin - lAttrib := FileGetAttr(Dir + ADirent^.d_name); - if ReturnAllSubDirs and ((lAttrib and faDirectory) <> 0) then - begin - SetLength(Result, i + 1); - Result[i].Name := ADirent^.d_name; - Result[i].IsDirectory := true; - Result[i].IsFile := false; - i := i + 1; - end - else if (Length(Filter) = 0) or (Pos( Filter, LowerCase(ADirent^.d_name)) > 0) then - begin - SetLength(Result, i + 1); - Result[i].Name := ADirent^.d_name; - Result[i].IsDirectory := false; - Result[i].IsFile := true; - i := i + 1; - end; - end; - until ADirent = nil; - - FPCloseDir(TheDir); + Result := GetApplicationSupportPath; end; end. diff --git a/Lua/src/base/UPlatformWindows.pas b/Lua/src/base/UPlatformWindows.pas index e198958a..a0372dad 100644 --- a/Lua/src/base/UPlatformWindows.pas +++ b/Lua/src/base/UPlatformWindows.pas @@ -38,21 +38,19 @@ interface uses Classes, - UPlatform; + UPlatform, + UPath; type TPlatformWindows = class(TPlatform) private - function GetSpecialPath(CSIDL: integer): WideString; + function GetSpecialPath(CSIDL: integer): IPath; public - function DirectoryFindFiles(Dir, Filter: WideString; ReturnAllSubDirs: Boolean): TDirectoryEntryArray; override; function TerminateIfAlreadyRunning(var WndTitle: String): Boolean; override; - function GetLogPath: WideString; override; - function GetGameSharedPath: WideString; override; - function GetGameUserPath: WideString; override; - - function CopyFile(const Source, Target: WideString; FailIfExists: boolean): boolean; override; + function GetLogPath: IPath; override; + function GetGameSharedPath: IPath; override; + function GetGameUserPath: IPath; override; end; implementation @@ -63,95 +61,6 @@ uses Windows, UConfig; -type - TSearchRecW = record - Time: Integer; - Size: Integer; - Attr: Integer; - Name: WideString; - ExcludeAttr: Integer; - FindHandle: THandle; - FindData: TWin32FindDataW; - end; - -function FindFirstW(const Path: WideString; Attr: Integer; var F: TSearchRecW): Integer; forward; -function FindNextW(var F: TSearchRecW): Integer; forward; -procedure FindCloseW(var F: TSearchRecW); forward; -function FindMatchingFileW(var F: TSearchRecW): Integer; forward; -function DirectoryExistsW(const Directory: widestring): Boolean; forward; - -function FindFirstW(const Path: widestring; Attr: Integer; var F: TSearchRecW): Integer; -const - faSpecial = faHidden or faSysFile or faVolumeID or faDirectory; -begin - F.ExcludeAttr := not Attr and faSpecial; -{$IFDEF Delphi} - F.FindHandle := FindFirstFileW(PWideChar(Path), F.FindData); -{$ELSE} - F.FindHandle := FindFirstFileW(PWideChar(Path), @F.FindData); -{$ENDIF} - if F.FindHandle <> INVALID_HANDLE_VALUE then - begin - Result := FindMatchingFileW(F); - if Result <> 0 then FindCloseW(F); - end else - Result := GetLastError; -end; - -function FindNextW(var F: TSearchRecW): Integer; -begin -{$IFDEF Delphi} - if FindNextFileW(F.FindHandle, F.FindData) then -{$ELSE} - if FindNextFileW(F.FindHandle, @F.FindData) then -{$ENDIF} - Result := FindMatchingFileW(F) - else - Result := GetLastError; -end; - -procedure FindCloseW(var F: TSearchRecW); -begin - if F.FindHandle <> INVALID_HANDLE_VALUE then - begin - Windows.FindClose(F.FindHandle); - F.FindHandle := INVALID_HANDLE_VALUE; - end; -end; - -function FindMatchingFileW(var F: TSearchRecW): Integer; -var - LocalFileTime: TFileTime; -begin - with F do - begin - while FindData.dwFileAttributes and ExcludeAttr <> 0 do -{$IFDEF Delphi} - if not FindNextFileW(FindHandle, FindData) then -{$ELSE} - if not FindNextFileW(FindHandle, @FindData) then -{$ENDIF} - begin - Result := GetLastError; - Exit; - end; - FileTimeToLocalFileTime(FindData.ftLastWriteTime, LocalFileTime); - FileTimeToDosDateTime(LocalFileTime, LongRec(Time).Hi, LongRec(Time).Lo); - Size := FindData.nFileSizeLow; - Attr := FindData.dwFileAttributes; - Name := FindData.cFileName; - end; - Result := 0; -end; - -function DirectoryExistsW(const Directory: widestring): Boolean; -var - Code: Integer; -begin - Code := GetFileAttributesW(PWideChar(Directory)); - Result := (Code <> -1) and (FILE_ATTRIBUTE_DIRECTORY and Code <> 0); -end; - //------------------------------ //Start more than One Time Prevention //------------------------------ @@ -180,41 +89,6 @@ begin end; end; -function TPlatformWindows.DirectoryFindFiles(Dir, Filter: WideString; ReturnAllSubDirs: Boolean): TDirectoryEntryArray; -var - i : Integer; - SR : TSearchRecW; - Attrib : Integer; -begin - i := 0; - Filter := LowerCase(Filter); - - if FindFirstW(Dir + '*', faAnyFile or faDirectory, SR) = 0 then - repeat - if (SR.Name <> '.') and (SR.Name <> '..') then - begin - Attrib := FileGetAttr(Dir + SR.name); - if ReturnAllSubDirs and ((Attrib and faDirectory) <> 0) then - begin - SetLength( Result, i + 1); - Result[i].Name := SR.name; - Result[i].IsDirectory := true; - Result[i].IsFile := false; - i := i + 1; - end - else if (Length(Filter) = 0) or (Pos( Filter, LowerCase(SR.Name)) > 0) then - begin - SetLength( Result, i + 1); - Result[i].Name := SR.Name; - Result[i].IsDirectory := false; - Result[i].IsFile := true; - i := i + 1; - end; - end; - until FindNextW(SR) <> 0; - FindCloseW(SR); -end; - (** * Returns the path of a special folder. * @@ -225,37 +99,30 @@ end; * CSIDL_PERSONAL (e.g. C:\Documents and Settings\username\My Documents) * CSIDL_MYMUSIC (e.g. C:\Documents and Settings\username\My Documents\My Music) *) -function TPlatformWindows.GetSpecialPath(CSIDL: integer): WideString; +function TPlatformWindows.GetSpecialPath(CSIDL: integer): IPath; var Buffer: array [0..MAX_PATH-1] of WideChar; begin -{$IF Defined(Delphi) or (FPC_VERSION_INT >= 2002002)} // >= 2.2.2 if (SHGetSpecialFolderPathW(0, @Buffer, CSIDL, false)) then - Result := Buffer + Result := Path(Buffer) else -{$IFEND} - Result := ''; + Result := PATH_NONE; end; -function TPlatformWindows.GetLogPath: WideString; +function TPlatformWindows.GetLogPath: IPath; begin Result := GetExecutionDir(); end; -function TPlatformWindows.GetGameSharedPath: WideString; +function TPlatformWindows.GetGameSharedPath: IPath; begin Result := GetExecutionDir(); end; -function TPlatformWindows.GetGameUserPath: WideString; +function TPlatformWindows.GetGameUserPath: IPath; begin - //Result := GetSpecialPath(CSIDL_APPDATA) + PathDelim + 'UltraStarDX' + PathDelim; + //Result := GetSpecialPath(CSIDL_APPDATA).Append('UltraStarDX', pdAppend); Result := GetExecutionDir(); end; -function TPlatformWindows.CopyFile(const Source, Target: WideString; FailIfExists: boolean): boolean; -begin - Result := Windows.CopyFileW(PWideChar(Source), PWideChar(Target), FailIfExists); -end; - end. diff --git a/Lua/src/base/UPlaylist.pas b/Lua/src/base/UPlaylist.pas index 11ed84de..527eca7b 100644 --- a/Lua/src/base/UPlaylist.pas +++ b/Lua/src/base/UPlaylist.pas @@ -34,20 +34,23 @@ interface {$I switches.inc} uses - USong; + Classes, + USong, + UPath, + UPathUtils; type TPlaylistItem = record - Artist: String; - Title: String; + Artist: UTF8String; + Title: UTF8String; SongID: Integer; end; APlaylistItem = array of TPlaylistItem; TPlaylist = record - Name: String; - Filename: String; + Name: UTF8String; + Filename: IPath; Items: APlaylistItem; end; @@ -67,20 +70,20 @@ type Playlists: APlaylist; constructor Create; - Procedure LoadPlayLists; - Function LoadPlayList(Index: Cardinal; Filename: String): Boolean; - Procedure SavePlayList(Index: Cardinal); + procedure LoadPlayLists; + function LoadPlayList(Index: Cardinal; const Filename: IPath): Boolean; + procedure SavePlayList(Index: Cardinal); - Procedure SetPlayList(Index: Cardinal); + procedure SetPlayList(Index: Cardinal); - Function AddPlaylist(Name: String): Cardinal; - Procedure DelPlaylist(const Index: Cardinal); + function AddPlaylist(const Name: UTF8String): Cardinal; + procedure DelPlaylist(const Index: Cardinal); - Procedure AddItem(const SongID: Cardinal; const iPlaylist: Integer = -1); - Procedure DelItem(const iItem: Cardinal; const iPlaylist: Integer = -1); + procedure AddItem(const SongID: Cardinal; const iPlaylist: Integer = -1); + procedure DelItem(const iItem: Cardinal; const iPlaylist: Integer = -1); - Procedure GetNames(var PLNames: array of String); - Function GetIndexbySongID(const SongID: Cardinal; const iPlaylist: Integer = -1): Integer; + procedure GetNames(var PLNames: array of UTF8String); + function GetIndexbySongID(const SongID: Cardinal; const iPlaylist: Integer = -1): Integer; end; {Modes: @@ -94,13 +97,15 @@ type implementation -uses USongs, - ULog, - UMain, - //UFiles, - UGraphic, - UThemes, - SysUtils; +uses + SysUtils, + USongs, + ULog, + UMain, + UFilesystem, + UGraphic, + UThemes, + UUnicodeUtils; //---------- //Create - Construct Class - Dummy for now @@ -116,90 +121,90 @@ end; //---------- Procedure TPlayListManager.LoadPlayLists; var - SR: TSearchRec; Len: Integer; PlayListBuffer: TPlayList; + Iter: IFileIterator; + FileInfo: TFileInfo; begin SetLength(Playlists, 0); - if FindFirst(PlayListPath + '*.upl', 0, SR) = 0 then + Iter := FileSystem.FileFind(PlayListPath.Append('*.upl'), 0); + while (Iter.HasNext) do begin - repeat - Len := Length(Playlists); - SetLength(Playlists, Len +1); + Len := Length(Playlists); + SetLength(Playlists, Len + 1); + + FileInfo := Iter.Next; - if not LoadPlayList (Len, Sr.Name) then - SetLength(Playlists, Len) - else + if not LoadPlayList(Len, FileInfo.Name) then + SetLength(Playlists, Len) + else + begin + // Sort the Playlists - Insertion Sort + PlayListBuffer := Playlists[Len]; + Dec(Len); + while (Len >= 0) AND (CompareText(Playlists[Len].Name, PlayListBuffer.Name) >= 0) do begin - // Sort the Playlists - Insertion Sort - PlayListBuffer := Playlists[Len]; - Dec(Len); - while (Len >= 0) AND (CompareText(Playlists[Len].Name, PlayListBuffer.Name) >= 0) do - begin - Playlists[Len+1] := Playlists[Len]; - Dec(Len); - end; - Playlists[Len+1] := PlayListBuffer; + Playlists[Len+1] := Playlists[Len]; + Dec(Len); end; - - until FindNext(SR) <> 0; - FindClose(SR); - end; + Playlists[Len+1] := PlayListBuffer; + end; + end; end; //---------- //LoadPlayList - Load a Playlist in the Array //---------- -Function TPlayListManager.LoadPlayList(Index: Cardinal; Filename: String): Boolean; - var - F: TextFile; - Line: String; - PosDelimiter: Integer; - SongID: Integer; - Len: Integer; +function TPlayListManager.LoadPlayList(Index: Cardinal; const Filename: IPath): Boolean; - Function FindSong(Artist, Title: String): Integer; + function FindSong(Artist, Title: UTF8String): Integer; var I: Integer; begin Result := -1; For I := low(CatSongs.Song) to high(CatSongs.Song) do begin - if (CatSongs.Song[I].Title = Title) AND (CatSongs.Song[I].Artist = Artist) then + if (CatSongs.Song[I].Title = Title) and (CatSongs.Song[I].Artist = Artist) then begin Result := I; Break; end; end; end; + +var + TextStream: TTextFileStream; + Line: UTF8String; + PosDelimiter: Integer; + SongID: Integer; + Len: Integer; + FilenameAbs: IPath; begin - if not FileExists(PlayListPath + Filename) then - begin - Log.LogError('Could not load Playlist: ' + Filename); - Result := False; - Exit; + //Load File + try + FilenameAbs := PlaylistPath.Append(Filename); + TextStream := TMemTextFileStream.Create(FilenameAbs, fmOpenRead); + except + begin + Log.LogError('Could not load Playlist: ' + FilenameAbs.ToNative); + Result := False; + Exit; + end; end; Result := True; - //Load File - AssignFile(F, PlayListPath + FileName); - Reset(F); - //Set Filename - PlayLists[Index].Filename := Filename; - PlayLists[Index].Name := ''; + Playlists[Index].Filename := Filename; + Playlists[Index].Name := ''; //Read Until End of File - While not Eof(F) do + while TextStream.ReadLine(Line) do begin - //Read Curent Line - Readln(F, Line); - if (Length(Line) > 0) then begin - PosDelimiter := Pos(':', Line); - if (PosDelimiter <> 0) then + PosDelimiter := UTF8Pos(':', Line); + if (PosDelimiter <> 0) then begin //Comment or Name String if (Line[1] = '#') then @@ -223,7 +228,7 @@ begin PlayLists[Index].Items[Len].Artist := Trim(copy(Line, 1, PosDelimiter - 1)); PlayLists[Index].Items[Len].Title := Trim(copy(Line, PosDelimiter + 1, Length(Line) - PosDelimiter)); end - else Log.LogError('Could not find Song in Playlist: ' + PlayLists[Index].Filename + ', ' + Line); + else Log.LogError('Could not find Song in Playlist: ' + PlayLists[Index].Filename.ToNative + ', ' + Line); end; end; end; @@ -232,71 +237,70 @@ begin //If no special name is given, use Filename if PlayLists[Index].Name = '' then begin - PlayLists[Index].Name := ChangeFileExt(FileName, ''); + PlayLists[Index].Name := FileName.SetExtension('').ToUTF8; end; //Finish (Close File) - CloseFile(F); + TextStream.Free; end; -//---------- -//SavePlayList - Saves the specified Playlist -//---------- -Procedure TPlayListManager.SavePlayList(Index: Cardinal); +{** + * Saves the specified Playlist + *} +procedure TPlayListManager.SavePlayList(Index: Cardinal); var - F: TextFile; + TextStream: TTextFileStream; + PlaylistFile: IPath; I: Integer; begin - if (Not FileExists(PlaylistPath + Playlists[Index].Filename)) OR (Not FileisReadOnly(PlaylistPath + Playlists[Index].Filename)) then - begin + PlaylistFile := PlaylistPath.Append(Playlists[Index].Filename); - //open File for Rewriting - AssignFile(F, PlaylistPath + Playlists[Index].Filename); - try - try - Rewrite(F); + // cannot update read-only file + if PlaylistFile.IsFile() and PlaylistFile.IsReadOnly() then + Exit; - //Write Version (not nessecary but helpful) - WriteLn(F, '######################################'); - WriteLn(F, '#Ultrastar Deluxe Playlist Format v1.0'); - WriteLn(F, '#Playlist "' + Playlists[Index].Name + '" with ' + InttoStr(Length(Playlists[Index].Items)) + ' Songs.'); - WriteLn(F, '######################################'); + // open file for rewriting + TextStream := TMemTextFileStream.Create(PlaylistFile, fmCreate); + try + // Write version (not nessecary but helpful) + TextStream.WriteLine('######################################'); + TextStream.WriteLine('#Ultrastar Deluxe Playlist Format v1.0'); + TextStream.WriteLine(Format('#Playlist %s with %d Songs.', + [ Playlists[Index].Name, Length(Playlists[Index].Items) ])); + TextStream.WriteLine('######################################'); - //Write Name Information - WriteLn(F, '#Name: ' + Playlists[Index].Name); + // Write name information + TextStream.WriteLine('#Name: ' + Playlists[Index].Name); - //Write Song Information - WriteLn(F, '#Songs:'); + // Write song information + TextStream.WriteLine('#Songs:'); - For I := 0 to high(Playlists[Index].Items) do - begin - WriteLn(F, Playlists[Index].Items[I].Artist + ' : ' + Playlists[Index].Items[I].Title); - end; - except - log.LogError('Could not write Playlistfile "' + Playlists[Index].Name + '"'); - end; - finally - CloseFile(F); + for I := 0 to high(Playlists[Index].Items) do + begin + TextStream.WriteLine(Playlists[Index].Items[I].Artist + ' : ' + Playlists[Index].Items[I].Title); end; + except + Log.LogError('Could not write Playlistfile "' + Playlists[Index].Name + '"'); end; + TextStream.Free; end; -//---------- -//SetPlayList - Display a Playlist in CatSongs -//---------- -Procedure TPlayListManager.SetPlayList(Index: Cardinal); +{** + * Display a Playlist in CatSongs + *} +procedure TPlayListManager.SetPlayList(Index: Cardinal); var I: Integer; begin - If (Int(Index) > High(PlayLists)) then + if (Int(Index) > High(PlayLists)) then exit; //Hide all Songs - For I := 0 to high(CatSongs.Song) do + for I := 0 to high(CatSongs.Song) do CatSongs.Song[I].Visible := False; //Show Songs in PL - For I := 0 to high(PlayLists[Index].Items) do + for I := 0 to high(PlayLists[Index].Items) do begin CatSongs.Song[PlayLists[Index].Items[I].SongID].Visible := True; end; @@ -313,7 +317,7 @@ begin //Fix SongSelection ScreenSong.Interaction := 0; - ScreenSong.SelectNext; + ScreenSong.SelectNext(true); ScreenSong.FixSelected; //Play correct Music @@ -323,31 +327,33 @@ end; //---------- //AddPlaylist - Adds a Playlist and Returns the Index //---------- -Function TPlayListManager.AddPlaylist(Name: String): Cardinal; +function TPlayListManager.AddPlaylist(const Name: UTF8String): cardinal; var I: Integer; + PlaylistFile: IPath; begin Result := Length(Playlists); SetLength(Playlists, Result + 1); // Sort the Playlists - Insertion Sort - while (Result > 0) AND (CompareText(Playlists[Result - 1].Name, Name) >= 0) do + while (Result > 0) and (CompareText(Playlists[Result - 1].Name, Name) >= 0) do begin Dec(Result); Playlists[Result+1] := Playlists[Result]; end; - Playlists[Result].Name := Name; + Playlists[Result].Name := Name; + + // clear playlist items + SetLength(Playlists[Result].Items, 0); I := 1; - if (not FileExists(PlaylistPath + Name + '.upl')) then - Playlists[Result].Filename := Name + '.upl' - else + PlaylistFile := PlaylistPath.Append(Name + '.upl'); + while (PlaylistFile.Exists) do begin - repeat - Inc(I); - until not FileExists(PlaylistPath + Name + InttoStr(I) + '.upl'); - Playlists[Result].Filename := Name + InttoStr(I) + '.upl'; + Inc(I); + PlaylistFile := PlaylistPath.Append(Name + InttoStr(I) + '.upl'); end; + Playlists[Result].Filename := PlaylistFile.GetName; //Save new Playlist SavePlayList(Result); @@ -356,28 +362,28 @@ end; //---------- //DelPlaylist - Deletes a Playlist //---------- -Procedure TPlayListManager.DelPlaylist(const Index: Cardinal); +procedure TPlayListManager.DelPlaylist(const Index: Cardinal); var I: Integer; - Filename: String; + Filename: IPath; begin - If Int(Index) > High(Playlists) then + if Int(Index) > High(Playlists) then Exit; - Filename := PlaylistPath + Playlists[Index].Filename; + Filename := PlaylistPath.Append(Playlists[Index].Filename); //If not FileExists or File is not Writeable then exit - If (Not FileExists(Filename)) OR (FileisReadOnly(Filename)) then + if (not Filename.IsFile()) or (Filename.IsReadOnly()) then Exit; //Delete Playlist from FileSystem - if Not DeleteFile(Filename) then + if not Filename.DeleteFile() then Exit; //Delete Playlist from Array //move all PLs to the Hole - For I := Index to High(Playlists)-1 do + for I := Index to High(Playlists)-1 do PlayLists[I] := PlayLists[I+1]; //Delete last Playlist @@ -389,7 +395,7 @@ begin begin ScreenSong.UnLoadDetailedCover; ScreenSong.HideCatTL; - CatSongs.SetFilter('', 0); + CatSongs.SetFilter('', fltAll); ScreenSong.Interaction := 0; ScreenSong.FixSelected; ScreenSong.ChangeMusic; @@ -470,7 +476,7 @@ end; //---------- //GetNames - Writes Playlist Names in a Array //---------- -Procedure TPlayListManager.GetNames(var PLNames: array of String); +procedure TPlayListManager.GetNames(var PLNames: array of UTF8String); var I: Integer; Len: Integer; diff --git a/Lua/src/base/UPluginInterface.pas b/Lua/src/base/UPluginInterface.pas deleted file mode 100644 index f299796f..00000000 --- a/Lua/src/base/UPluginInterface.pas +++ /dev/null @@ -1,186 +0,0 @@ -{* 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 uPluginInterface; -{********************* - uPluginInterface - Unit fills a TPluginInterface structure with method pointers - Unit contains all functions called directly by plugins -*********************} - -interface - -{$IFDEF FPC} - {$MODE Delphi} -{$ENDIF} - -{$I switches.inc} - -uses - uPluginDefs; - -//--------------- -// Methods for Plugin -//--------------- - {******** Hook specific Methods ********} - {Function Creates a new Hookable Event and Returns the Handle - or 0 on Failure. (Name already exists)} - Function CreateHookableEvent (EventName: PChar): THandle; stdcall; - - {Function Destroys an Event and Unhooks all Hooks to this Event. - 0 on success, not 0 on Failure} - Function DestroyHookableEvent (hEvent: THandle): integer; stdcall; - - {Function start calling the Hook Chain - 0 if Chain is called until the End, -1 if Event Handle is not valid - otherwise Return Value of the Hook that breaks the Chain} - Function NotivyEventHooks (hEvent: THandle; wParam: TwParam; lParam: TlParam): integer; stdcall; - - {Function Hooks an Event by Name. - Returns Hook Handle on Success, otherwise 0} - Function HookEvent (EventName: PChar; HookProc: TUS_Hook): THandle; stdcall; - - {Function Removes the Hook from the Chain - Returns 0 on Success} - Function UnHookEvent (hHook: THandle): Integer; stdcall; - - {Function Returns Non Zero if a Event with the given Name Exists, - otherwise 0} - Function EventExists (EventName: PChar): Integer; stdcall; - - {******** Service specific Methods ********} - {Function Creates a new Service and Returns the Services Handle - or 0 on Failure. (Name already exists)} - Function CreateService (ServiceName: PChar; ServiceProc: TUS_Service): THandle; stdcall; - - {Function Destroys a Service. - 0 on success, not 0 on Failure} - Function DestroyService (hService: THandle): integer; stdcall; - - {Function Calls a Services Proc - Returns Services Return Value or SERVICE_NOT_FOUND on Failure} - Function CallService (ServiceName: PChar; wParam: TwParam; lParam: TlParam): integer; stdcall; - - {Function Returns Non Zero if a Service with the given Name Exists, - otherwise 0} - Function ServiceExists (ServiceName: PChar): Integer; stdcall; - -implementation -uses UCore; - -{******** Hook specific Methods ********} -//--------------- -// Function Creates a new Hookable Event and Returns the Handle -// or 0 on Failure. (Name already exists) -//--------------- -Function CreateHookableEvent (EventName: PChar): THandle; stdcall; -begin - Result := Core.Hooks.AddEvent(EventName); -end; - -//--------------- -// Function Destroys an Event and Unhooks all Hooks to this Event. -// 0 on success, not 0 on Failure -//--------------- -Function DestroyHookableEvent (hEvent: THandle): integer; stdcall; -begin - Result := Core.Hooks.DelEvent(hEvent); -end; - -//--------------- -// Function start calling the Hook Chain -// 0 if Chain is called until the End, -1 if Event Handle is not valid -// otherwise Return Value of the Hook that breaks the Chain -//--------------- -Function NotivyEventHooks (hEvent: THandle; wParam: TwParam; lParam: TlParam): integer; stdcall; -begin - Result := Core.Hooks.CallEventChain(hEvent, wParam, lParam); -end; - -//--------------- -// Function Hooks an Event by Name. -// Returns Hook Handle on Success, otherwise 0 -//--------------- -Function HookEvent (EventName: PChar; HookProc: TUS_Hook): THandle; stdcall; -begin - Result := Core.Hooks.AddSubscriber(EventName, HookProc); -end; - -//--------------- -// Function Removes the Hook from the Chain -// Returns 0 on Success -//--------------- -Function UnHookEvent (hHook: THandle): Integer; stdcall; -begin - Result := Core.Hooks.DelSubscriber(hHook); -end; - -//--------------- -// Function Returns Non Zero if a Event with the given Name Exists, -// otherwise 0 -//--------------- -Function EventExists (EventName: PChar): Integer; stdcall; -begin - Result := Core.Hooks.EventExists(EventName); -end; - - {******** Service specific Methods ********} -//--------------- -// Function Creates a new Service and Returns the Services Handle -// or 0 on Failure. (Name already exists) -//--------------- -Function CreateService (ServiceName: PChar; ServiceProc: TUS_Service): THandle; stdcall; -begin - Result := Core.Services.AddService(ServiceName, ServiceProc); -end; - -//--------------- -// Function Destroys a Service. -// 0 on success, not 0 on Failure -//--------------- -Function DestroyService (hService: THandle): integer; stdcall; -begin - Result := Core.Services.DelService(hService); -end; - -//--------------- -// Function Calls a Services Proc -// Returns Services Return Value or SERVICE_NOT_FOUND on Failure -//--------------- -Function CallService (ServiceName: PChar; wParam: TwParam; lParam: TlParam): integer; stdcall; -begin - Result := Core.Services.CallService(ServiceName, wParam, lParam); -end; - -//--------------- -// Function Returns Non Zero if a Service with the given Name Exists, -// otherwise 0 -//--------------- -Function ServiceExists (ServiceName: PChar): Integer; stdcall; -begin - Result := Core.Services.ServiceExists(ServiceName); -end; - -end. diff --git a/Lua/src/base/UPluginLoader.pas b/Lua/src/base/UPluginLoader.pas deleted file mode 100644 index 5e581c23..00000000 --- a/Lua/src/base/UPluginLoader.pas +++ /dev/null @@ -1,798 +0,0 @@ -{* 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: https://ultrastardx.svn.sourceforge.net/svnroot/ultrastardx/trunk/src/base/uPluginLoader.pas $ - * $Id: uPluginLoader.pas 1403 2008-09-23 21:17:22Z k-m_schindler $ - *} - -unit UPluginLoader; -{********************* - UPluginLoader - Unit contains two classes - TPluginLoader: Class searching for and loading the plugins - TtehPlugins: Class representing the plugins in modules chain -*********************} - -interface - -{$IFDEF FPC} - {$MODE Delphi} -{$ENDIF} - -{$I switches.inc} - -uses - UPluginDefs, - UCoreModule; - -type - TPluginListItem = record - Info: TUS_PluginInfo; - State: Byte; // State of this plugin: 0 - undefined; 1 - loaded; 2 - inited / running; 4 - unloaded; 254 - loading aborted by plugin; 255 - unloaded because of error - Path: String; // path to this plugin - NeedsDeInit: Boolean; // if this is inited correctly this should be true - hLib: THandle; // handle of loaded libary - Procs: record // procs offered by plugin. Don't call this directly use wrappers of TPluginLoader - Load: Func_Load; - Init: Func_Init; - DeInit: Proc_DeInit; - end; - end; - {********************* - TPluginLoader - Class searches for plugins and manages loading and unloading - *********************} - PPluginLoader = ^TPluginLoader; - TPluginLoader = class (TCoreModule) - private - LoadingProcessFinished: Boolean; - sUnloadPlugin: THandle; - sLoadPlugin: THandle; - sGetPluginInfo: THandle; - sGetPluginState: THandle; - - procedure FreePlugin(Index: Cardinal); - public - PluginInterface: TUS_PluginInterface; - Plugins: array of TPluginListItem; - - // TCoreModule methods to inherit - constructor Create; override; - procedure Info(const pInfo: PModuleInfo); override; - function Load: Boolean; override; - function Init: Boolean; override; - procedure DeInit; override; - Destructor Destroy; override; - - // New methods - procedure BrowseDir(Path: String); // browses the path at _Path_ for plugins - function PluginExists(Name: String): integer; // if plugin exists: Index of plugin, else -1 - procedure AddPlugin(Filename: String); // adds plugin to the array - - function CallLoad(Index: Cardinal): integer; - function CallInit(Index: Cardinal): integer; - procedure CallDeInit(Index: Cardinal); - - //Services offered - function LoadPlugin(wParam: TwParam; lParam: TlParam): integer; //wParam PChar(PluginName/PluginPath) | lParam (if wParam = nil) ID of the Plugin - function UnloadPlugin(wParam: TwParam; lParam: TlParam): integer; //wParam PChar(PluginName/PluginPath) | lParam (if wParam = nil) ID of the Plugin - function GetPluginInfo(wParam: TwParam; lParam: TlParam): integer; //If wParam = -1 then (If lParam = nil then get length of Moduleinfo Array. If lparam <> nil then write array of TUS_PluginInfo to address at lparam) else (Get PluginInfo of Plugin with Index(wParam) to Address at lParam) - function GetPluginState(wParam: TwParam; lParam: TlParam): integer; //If wParam = -1 then (If lParam = nil then get length of Moduleinfo Array. If lparam <> nil then write array of TUS_PluginInfo to address at lparam) else (Return PluginInfo of Plugin with Index(wParam)) - - end; - - {********************* - TtehPlugins - Class represents the plugins in module chain. - It calls the plugins procs and funcs - *********************} - TtehPlugins = class (TCoreModule) - private - PluginLoader: PPluginLoader; - public - // TCoreModule methods to inherit - constructor Create; override; - - procedure Info(const pInfo: PModuleInfo); override; - function Load: Boolean; override; - function Init: Boolean; override; - procedure DeInit; override; - end; - -const -{$IF Defined(MSWINDOWS)} - PluginFileExtension = '.dll'; -{$ELSEIF Defined(DARWIN)} - PluginFileExtension = '.dylib'; -{$ELSEIF Defined(UNIX)} - PluginFileExtension = '.so'; -{$IFEND} - -implementation - -uses - UCore, - UPluginInterface, -{$IFDEF MSWINDOWS} - windows, -{$ELSE} - dynlibs, -{$ENDIF} - UMain, - SysUtils; - -{********************* - TPluginLoader - Implementation -*********************} - -//------------- -// function that gives some infos about the module to the core -//------------- -procedure TPluginLoader.Info(const pInfo: PModuleInfo); -begin - pInfo^.Name := 'TPluginLoader'; - pInfo^.Version := MakeVersion(1,0,0,chr(0)); - pInfo^.Description := 'Searches for plugins, loads and unloads them'; -end; - -//------------- -// Just the constructor -//------------- -constructor TPluginLoader.Create; -begin - inherited; - - // Init PluginInterface - // Using methods from UPluginInterface - PluginInterface.CreateHookableEvent := CreateHookableEvent; - PluginInterface.DestroyHookableEvent := DestroyHookableEvent; - PluginInterface.NotivyEventHooks := NotivyEventHooks; - PluginInterface.HookEvent := HookEvent; - PluginInterface.UnHookEvent := UnHookEvent; - PluginInterface.EventExists := EventExists; - - PluginInterface.CreateService := @CreateService; - PluginInterface.DestroyService := DestroyService; - PluginInterface.CallService := CallService; - PluginInterface.ServiceExists := ServiceExists; - - // UnSet private var - LoadingProcessFinished := False; -end; - -//------------- -// Is called on loading. -// In this method only events and services should be created -// to offer them to other modules or plugins during the init process -// if false is returned this will cause a forced exit -//------------- -function TPluginLoader.Load: Boolean; -begin - Result := True; - - try - // Start searching for plugins - BrowseDir(PluginPath); - except - Result := False; - Core.ReportError(integer(PChar('Error browsing and loading.')), PChar('TPluginLoader')); - end; -end; - -//------------- -// Is called on init process -// In this method you can hook some events and create + init -// your classes, variables etc. -// If false is returned this will cause a forced exit -//------------- -function TPluginLoader.Init: Boolean; -begin - // Just set private var to true. - LoadingProcessFinished := True; - Result := True; -end; - -//------------- -// Is called if this module has been inited and there is a exit. -// Deinit is in backwards initing order -//------------- -procedure TPluginLoader.DeInit; -var - I: integer; -begin - // Force deinit - // if some plugins aren't deinited for some reason o0 - for I := 0 to High(Plugins) do - begin - if (Plugins[I].State < 4) then - FreePlugin(I); - end; - - // Nothing to do here. Core will remove the hooks -end; - -//------------- -// Is called if this module will be unloaded and has been created -// Should be used to free memory -//------------- -Destructor TPluginLoader.Destroy; -begin - // Just save some memory if it wasn't done now.. - SetLength(Plugins, 0); - inherited; -end; - -//-------------- -// Browses the path at _Path_ for plugins -//-------------- -procedure TPluginLoader.BrowseDir(Path: String); -var - SR: TSearchRec; -begin - // Search for other dirs to browse - if FindFirst(Path + '*', faDirectory, SR) = 0 then begin - repeat - if (SR.Name <> '.') and (SR.Name <> '..') then - BrowseDir(Path + Sr.Name + PathDelim); - until FindNext(SR) <> 0; - end; - FindClose(SR); - - // Search for plugins at path - if FindFirst(Path + '*' + PluginFileExtension, 0, SR) = 0 then - begin - repeat - AddPlugin(Path + SR.Name); - until FindNext(SR) <> 0; - end; - FindClose(SR); -end; - -//-------------- -// If plugin exists: Index of plugin, else -1 -//-------------- -function TPluginLoader.PluginExists(Name: String): integer; -var - I: integer; -begin - Result := -1; - - if (Length(Name) <= 32 { =>Length(TUS_PluginInfo.Name)}) then - begin - for I := 0 to High(Plugins) do - if (Plugins[I].Info.Name = Name) then - begin //Found the Plugin - Result := I; - Break; - end; - end; -end; - -//-------------- -// Adds plugin to the array -//-------------- -procedure TPluginLoader.AddPlugin(Filename: String); -var - hLib: THandle; - PInfo: Proc_PluginInfo; - Info: TUS_PluginInfo; - PluginID: integer; -begin - if (FileExists(Filename)) then - begin //Load Libary - hLib := LoadLibrary(PChar(Filename)); - if (hLib <> 0) then - begin // Try to get address of the info proc - PInfo := GetProcAddress (hLib, PChar('USPlugin_Info')); - if (@PInfo <> nil) then - begin - Info.cbSize := SizeOf(TUS_PluginInfo); - - try // Call info proc - PInfo(@Info); - except - Info.Name := ''; - Core.ReportError(integer(PChar('Error getting plugin info: ' + Filename)), PChar('TPluginLoader')); - end; - - // Is name set ? - if (Trim(Info.Name) <> '') then - begin - PluginID := PluginExists(Info.Name); - - if (PluginID > 0) and (Plugins[PluginID].State >=4) then - PluginID := -1; - - if (PluginID = -1) then - begin - // Add new item to array - PluginID := Length(Plugins); - SetLength(Plugins, PluginID + 1); - - // Fill with info: - Plugins[PluginID].Info := Info; - Plugins[PluginID].State := 0; - Plugins[PluginID].Path := Filename; - Plugins[PluginID].NeedsDeInit := False; - Plugins[PluginID].hLib := hLib; - - // Try to get procs - Plugins[PluginID].Procs.Load := GetProcAddress (hLib, PChar('USPlugin_Load')); - Plugins[PluginID].Procs.Init := GetProcAddress (hLib, PChar('USPlugin_Init')); - Plugins[PluginID].Procs.DeInit := GetProcAddress (hLib, PChar('USPlugin_DeInit')); - - if (@Plugins[PluginID].Procs.Load = nil) OR (@Plugins[PluginID].Procs.Init = nil) OR (@Plugins[PluginID].Procs.DeInit = nil) then - begin - Plugins[PluginID].State := 255; - FreeLibrary(hLib); - Core.ReportError(integer(PChar('Can''t get plugin procs from libary: "' + Info.Name + '" ' + Filename)), PChar('TPluginLoader')); - end; - - // Emulate loading process if this plugin is loaded too late - if (LoadingProcessFinished) then - begin - CallLoad(PluginID); - CallInit(PluginID); - end; - end - else if (LoadingProcessFinished = False) then - begin - if (Plugins[PluginID].Info.Version < Info.Version) then - begin // Found newer version of this plugin - Core.ReportDebug(integer(PChar('Found a newer version of plugin: ' + String(Info.Name))), PChar('TPluginLoader')); - - // Unload old plugin - UnloadPlugin(PluginID, nil); - - // Fill with new info - Plugins[PluginID].Info := Info; - Plugins[PluginID].State := 0; - Plugins[PluginID].Path := Filename; - Plugins[PluginID].NeedsDeInit := False; - Plugins[PluginID].hLib := hLib; - - // Try to get procs - Plugins[PluginID].Procs.Load := GetProcAddress (hLib, PChar('USPlugin_Load')); - Plugins[PluginID].Procs.Init := GetProcAddress (hLib, PChar('USPlugin_Init')); - Plugins[PluginID].Procs.DeInit := GetProcAddress (hLib, PChar('USPlugin_DeInit')); - - if (@Plugins[PluginID].Procs.Load = nil) OR (@Plugins[PluginID].Procs.Init = nil) OR (@Plugins[PluginID].Procs.DeInit = nil) then - begin - FreeLibrary(hLib); - Plugins[PluginID].State := 255; - Core.ReportError(integer(PChar('Can''t get plugin procs from libary: "' + Info.Name + '" ' + Filename)), PChar('TPluginLoader')); - end; - end - else - begin // Newer Version already loaded - FreeLibrary(hLib); - end; - end - else - begin - FreeLibrary(hLib); - Core.ReportError(integer(PChar('Plugin with this name already exists: ' + String(Info.Name))), PChar('TPluginLoader')); - end; - end - else - begin - FreeLibrary(hLib); - Core.ReportError(integer(PChar('No name reported: ' + Filename)), PChar('TPluginLoader')); - end; - end - else - begin - FreeLibrary(hLib); - Core.ReportError(integer(PChar('Can''t find info procedure: ' + Filename)), PChar('TPluginLoader')); - end; - end - else - Core.ReportError(integer(PChar('Can''t load plugin libary: ' + Filename)), PChar('TPluginLoader')); - end; -end; - -//-------------- -// Calls load func of plugin with the given index -//-------------- -function TPluginLoader.CallLoad(Index: Cardinal): integer; -begin - Result := -2; - if(Index < Length(Plugins)) then - begin - if (@Plugins[Index].Procs.Load <> nil) and (Plugins[Index].State = 0) then - begin - try - Result := Plugins[Index].Procs.Load(@PluginInterface); - except - Result := -3; - End; - - if (Result = 0) then - Plugins[Index].State := 1 - else - begin - FreePlugin(Index); - Plugins[Index].State := 255; - Core.ReportError(integer(PChar('Error calling load function from plugin: ' + String(Plugins[Index].Info.Name))), PChar('TPluginLoader')); - end; - end; - end; -end; - -//-------------- -// Calls init func of plugin with the given index -//-------------- -function TPluginLoader.CallInit(Index: Cardinal): integer; -begin - Result := -2; - if(Index < Length(Plugins)) then - begin - if (@Plugins[Index].Procs.Init <> nil) and (Plugins[Index].State = 1) then - begin - try - Result := Plugins[Index].Procs.Init(@PluginInterface); - except - Result := -3; - End; - - if (Result = 0) then - begin - Plugins[Index].State := 2; - Plugins[Index].NeedsDeInit := True; - end - else - begin - FreePlugin(Index); - Plugins[Index].State := 255; - Core.ReportError(integer(PChar('Error calling init function from plugin: ' + String(Plugins[Index].Info.Name))), PChar('TPluginLoader')); - end; - end; - end; -end; - -//-------------- -// Calls deinit proc of plugin with the given index -//-------------- -procedure TPluginLoader.CallDeInit(Index: Cardinal); -begin - if(Index < Length(Plugins)) then - begin - if (Plugins[Index].State < 4) then - begin - if (@Plugins[Index].Procs.DeInit <> nil) and (Plugins[Index].NeedsDeInit) then - try - Plugins[Index].Procs.DeInit(@PluginInterface); - except - - End; - - // Don't forget to remove services and subscriptions by this plugin - Core.Hooks.DelbyOwner(-1 - Index); - - FreePlugin(Index); - end; - end; -end; - -//-------------- -// Frees all plugin sources (procs and handles) - helper for deiniting functions -//-------------- -procedure TPluginLoader.FreePlugin(Index: Cardinal); -begin - Plugins[Index].State := 4; - Plugins[Index].Procs.Load := nil; - Plugins[Index].Procs.Init := nil; - Plugins[Index].Procs.DeInit := nil; - - if (Plugins[Index].hLib <> 0) then - FreeLibrary(Plugins[Index].hLib); -end; - - - -//-------------- -// wParam PChar(PluginName/PluginPath) | wParam (if lParam = nil) ID of the plugin -//-------------- -function TPluginLoader.LoadPlugin(wParam: TwParam; lParam: TlParam): integer; -var - Index: integer; - sFile: String; -begin - Result := -1; - sFile := ''; - // lParam is ID - if (lParam = nil) then - begin - Index := wParam; - end - else - begin //lParam is PChar - try - sFile := String(PChar(lParam)); - Index := PluginExists(sFile); - if (Index < 0) And FileExists(sFile) then - begin // Is filename - AddPlugin(sFile); - Result := Plugins[High(Plugins)].State; - end; - except - Index := -2; - end; - end; - - - if (Index >= 0) and (Index < Length(Plugins)) then - begin - AddPlugin(Plugins[Index].Path); - Result := Plugins[Index].State; - end; -end; - -//-------------- -// wParam PChar(PluginName/PluginPath) | wParam (if lParam = nil) ID of the plugin -//-------------- -function TPluginLoader.UnloadPlugin(wParam: TwParam; lParam: TlParam): integer; -var - Index: integer; - sName: String; -begin - Result := -1; - // lParam is ID - if (lParam = nil) then - begin - Index := wParam; - end - else - begin // wParam is PChar - try - sName := String(PChar(lParam)); - Index := PluginExists(sName); - except - Index := -2; - end; - end; - - - if (Index >= 0) and (Index < Length(Plugins)) then - CallDeInit(Index) -end; - -//-------------- -// if wParam = -1 then (if lParam = nil then get length of moduleinfo array. if lparam <> nil then write array of TUS_PluginInfo to address at lparam) else (Get PluginInfo of plugin with Index(wParam) to address at lParam) -//-------------- -function TPluginLoader.GetPluginInfo(wParam: TwParam; lParam: TlParam): integer; -var I: integer; -begin - Result := 0; - if (wParam > 0) then - begin // Get info of 1 plugin - if (lParam <> nil) and (wParam < Length(Plugins)) then - begin - try - Result := 1; - PUS_PluginInfo(lParam)^ := Plugins[wParam].Info; - except - - End; - end; - end - else if (lParam = nil) then - begin // Get length of plugin (info) array - Result := Length(Plugins); - end - else //Write PluginInfo Array to Address in lParam - begin - try - for I := 0 to high(Plugins) do - PAUS_PluginInfo(lParam)^[I] := Plugins[I].Info; - Result := Length(Plugins); - except - Core.ReportError(integer(PChar('Could not write PluginInfo Array')), PChar('TPluginLoader')); - End; - end; - -end; - -//-------------- -// if wParam = -1 then (if lParam = nil then get length of plugin state array. if lparam <> nil then write array of Byte to address at lparam) else (return state of plugin with index(wParam)) -//-------------- -function TPluginLoader.GetPluginState(wParam: TwParam; lParam: TlParam): integer; -var I: integer; -begin - Result := -1; - if (wParam > 0) then - begin // Get state of 1 plugin - if (wParam < Length(Plugins)) then - begin - Result := Plugins[wParam].State; - end; - end - else if (lParam = nil) then - begin // Get length of plugin (info) array - Result := Length(Plugins); - end - else // Write plugininfo array to address in lParam - begin - try - for I := 0 to high(Plugins) do - Byte(Pointer(integer(lParam) + I)^) := Plugins[I].State; - Result := Length(Plugins); - except - Core.ReportError(integer(PChar('Could not write pluginstate array')), PChar('TPluginLoader')); - End; - end; -end; - - -{********************* - TtehPlugins - Implementation -*********************} - -//------------- -// function that gives some infos about the module to the core -//------------- -procedure TtehPlugins.Info(const pInfo: PModuleInfo); -begin - pInfo^.Name := 'TtehPlugins'; - pInfo^.Version := MakeVersion(1,0,0,chr(0)); - pInfo^.Description := 'Module executing the Plugins!'; -end; - -//------------- -// Just the constructor -//------------- -constructor TtehPlugins.Create; -begin - inherited; - PluginLoader := nil; -end; - -//------------- -// Is called on loading. -// In this method only events and services should be created -// to offer them to other modules or plugins during the init process -// if false is returned this will cause a forced exit -//------------- -function TtehPlugins.Load: Boolean; -var - i: integer; // Counter - CurExecutedBackup: integer; //backup of Core.CurExecuted Attribute -begin - // Get pointer to pluginloader - PluginLoader := PPluginLoader(Core.GetModulebyName('TPluginLoader')); - if (PluginLoader = nil) then - begin - Result := false; - Core.ReportError(integer(PChar('Could not get pointer to pluginLoader')), PChar('TtehPlugins')); - end - else - begin - Result := true; - - // Backup curexecuted - CurExecutedBackup := Core.CurExecuted; - - // Start loading the plugins - for i := 0 to High(PluginLoader.Plugins) do - begin - Core.CurExecuted := -1 - i; - - try - // Unload plugin if not correctly executed - if (PluginLoader.CallLoad(i) <> 0) then - begin - PluginLoader.CallDeInit(i); - PluginLoader.Plugins[i].State := 254; // Plugin asks for unload - Core.ReportDebug(integer(PChar('Plugin selfabort during loading process: ' + String(PluginLoader.Plugins[i].Info.Name))), PChar('TtehPlugins')); - end - else - begin - Core.ReportDebug(integer(PChar('Plugin loaded succesfully: ' + String(PluginLoader.Plugins[i].Info.Name))), PChar('TtehPlugins')); - end; - except - // Plugin could not be loaded. - // => Show error message, then shutdown plugin - on E: Exception do - begin - PluginLoader.CallDeInit(i); - PluginLoader.Plugins[i].State := 255; // Plugin causes error - Core.ReportError(integer(PChar('Plugin causes error during loading process: ' + PluginLoader.Plugins[i].Info.Name + ', ErrorMsg: "' + E.Message + '"')), PChar('TtehPlugins')); - end; - end; - end; - - // Reset CurExecuted - Core.CurExecuted := CurExecutedBackup; - end; -end; - -//------------- -// Is called on init process -// in this method you can hook some events and create + init -// your classes, variables etc. -// if false is returned this will cause a forced exit -//------------- -function TtehPlugins.Init: Boolean; -var - i: integer; // Counter - CurExecutedBackup: integer; // backup of Core.CurExecuted attribute -begin - Result := true; - - // Backup CurExecuted - CurExecutedBackup := Core.CurExecuted; - - // Start loading the plugins - for i := 0 to High(PluginLoader.Plugins) do - try - Core.CurExecuted := -1 - i; - - // Unload plugin if not correctly executed - if (PluginLoader.CallInit(i) <> 0) then - begin - PluginLoader.CallDeInit(i); - PluginLoader.Plugins[i].State := 254; //Plugin asks for unload - Core.ReportDebug(integer(PChar('Plugin selfabort during init process: ' + String(PluginLoader.Plugins[i].Info.Name))), PChar('TtehPlugins')); - end - else - Core.ReportDebug(integer(PChar('Plugin inited succesfully: ' + String(PluginLoader.Plugins[i].Info.Name))), PChar('TtehPlugins')); - except - // Plugin could not be loaded. - // => Show error message, then shut down plugin - PluginLoader.CallDeInit(i); - PluginLoader.Plugins[i].State := 255; //Plugin causes Error - Core.ReportError(integer(PChar('Plugin causes error during init process: ' + String(PluginLoader.Plugins[i].Info.Name))), PChar('TtehPlugins')); - end; - - // Reset CurExecuted - Core.CurExecuted := CurExecutedBackup; -end; - -//------------- -// Is called if this module has been inited and there is a exit. -// Deinit is in backwards initing order -//------------- -procedure TtehPlugins.DeInit; -var - i: integer; // Counter - CurExecutedBackup: integer; // backup of Core.CurExecuted attribute -begin - // Backup CurExecuted - CurExecutedBackup := Core.CurExecuted; - - // Start loop - - for i := 0 to High(PluginLoader.Plugins) do - begin - try - // DeInit plugin - PluginLoader.CallDeInit(i); - except - end; - end; - - // Reset CurExecuted - Core.CurExecuted := CurExecutedBackup; -end; - -end. diff --git a/Lua/src/base/URecord.pas b/Lua/src/base/URecord.pas index 132bafd5..2c2093a0 100644 --- a/Lua/src/base/URecord.pas +++ b/Lua/src/base/URecord.pas @@ -36,26 +36,26 @@ interface uses Classes, Math, - SysUtils, sdl, + SysUtils, UCommon, UMusic, UIni; const BaseToneFreq = 65.4064; // lowest (half-)tone to analyze (C2 = 65.4064 Hz) - NumHalftones = 36; // C2-B4 (for Whitney and my high voice) + NumHalftones = 36; // C2-B4 (for Whitney and my high voice) type TCaptureBuffer = class private - VoiceStream: TAudioVoiceStream; // stream for voice passthrough + VoiceStream: TAudioVoiceStream; // stream for voice passthrough AnalysisBufferLock: PSDL_Mutex; function GetToneString: string; // converts a tone to its string represenatation; - procedure BoostBuffer(Buffer: PChar; Size: Cardinal); - procedure ProcessNewBuffer(Buffer: PChar; BufferSize: integer); + procedure BoostBuffer(Buffer: PByteArray; Size: integer); + procedure ProcessNewBuffer(Buffer: PByteArray; BufferSize: integer); // we call it to analyze sound by checking Autocorrelation procedure AnalyzeByAutocorrelation; @@ -86,7 +86,7 @@ type procedure LockAnalysisBuffer(); {$IFDEF HasInline}inline;{$ENDIF} procedure UnlockAnalysisBuffer(); {$IFDEF HasInline}inline;{$ENDIF} - function MaxSampleVolume: Single; + function MaxSampleVolume: single; property ToneString: string READ GetToneString; end; @@ -95,17 +95,17 @@ const type TAudioInputSource = record - Name: string; + Name: string; end; // soundcard input-devices information TAudioInputDevice = class public - CfgIndex: integer; // index of this device in Ini.InputDeviceConfig - Name: string; // soundcard name - Source: array of TAudioInputSource; // soundcard input-sources - SourceRestore: integer; // source-index that will be selected after capturing (-1: not detected) - MicSource: integer; // source-index of mic (-1: none detected) + CfgIndex: integer; // index of this device in Ini.InputDeviceConfig + Name: string; // soundcard name + Source: array of TAudioInputSource; // soundcard input-sources + SourceRestore: integer; // source-index that will be selected after capturing (-1: not detected) + MicSource: integer; // source-index of mic (-1: none detected) AudioFormat: TAudioFormatInfo; // capture format info (e.g. 44.1kHz SInt16 stereo) CaptureChannel: array of TCaptureBuffer; // sound-buffer references used for mono or stereo channel's capture data @@ -115,10 +115,10 @@ type procedure LinkCaptureBuffer(ChannelIndex: integer; Sound: TCaptureBuffer); // TODO: add Open/Close functions so Start/Stop becomes faster - //function Open(): boolean; virtual; abstract; - //function Close(): boolean; virtual; abstract; - function Start(): boolean; virtual; abstract; - function Stop(): boolean; virtual; abstract; + //function Open(): boolean; virtual; abstract; + //function Close(): boolean; virtual; abstract; + function Start(): boolean; virtual; abstract; + function Stop(): boolean; virtual; abstract; function GetVolume(): single; virtual; abstract; procedure SetVolume(Volume: single); virtual; abstract; @@ -135,7 +135,7 @@ type procedure UpdateInputDeviceConfig; // handle microphone input - procedure HandleMicrophoneData(Buffer: PChar; Size: Cardinal; + procedure HandleMicrophoneData(Buffer: PByteArray; Size: integer; InputDevice: TAudioInputDevice); end; @@ -153,7 +153,6 @@ type procedure CaptureStop; end; - TSmallIntArray = array [0..(MaxInt div SizeOf(SmallInt))-1] of SmallInt; PSmallIntArray = ^TSmallIntArray; @@ -163,12 +162,11 @@ implementation uses ULog, - UMain; + UNote; var singleton_AudioInputProcessor : TAudioInputProcessor = nil; - { Global } function AudioInputProcessor(): TAudioInputProcessor; @@ -179,7 +177,6 @@ begin result := singleton_AudioInputProcessor; end; - { TAudioInputDevice } destructor TAudioInputDevice.Destroy; @@ -268,11 +265,11 @@ begin UnlockAnalysisBuffer(); end; -procedure TCaptureBuffer.ProcessNewBuffer(Buffer: PChar; BufferSize: integer); +procedure TCaptureBuffer.ProcessNewBuffer(Buffer: PByteArray; BufferSize: integer); var BufferOffset: integer; - SampleCount: integer; - i: integer; + SampleCount: integer; + i: integer; begin // apply software boost BoostBuffer(Buffer, BufferSize); @@ -299,7 +296,6 @@ begin SampleCount := Length(AnalysisBuffer); end; - LockAnalysisBuffer(); try @@ -315,7 +311,6 @@ begin UnlockAnalysisBuffer(); end; - // save capture-data to BufferLong if enabled if (Ini.SavePlayback = 1) then begin @@ -329,10 +324,10 @@ end; procedure TCaptureBuffer.AnalyzeBuffer; var - Volume: single; - MaxVolume: single; + Volume: single; + MaxVolume: single; SampleIndex: integer; - Threshold: single; + Threshold: single; begin ToneValid := false; ToneAbs := -1; @@ -431,10 +426,10 @@ begin Result := 1 - AccumDist / AnalysisBufferSize; end; -function TCaptureBuffer.MaxSampleVolume: Single; +function TCaptureBuffer.MaxSampleVolume: single; var - lSampleIndex: Integer; - lMaxVol : Longint; + lSampleIndex: integer; + lMaxVol: longint; begin; LockAnalysisBuffer(); try @@ -464,13 +459,13 @@ begin Result := '-'; end; -procedure TCaptureBuffer.BoostBuffer(Buffer: PChar; Size: Cardinal); +procedure TCaptureBuffer.BoostBuffer(Buffer: PByteArray; Size: integer); var - i: integer; - Value: Longint; - SampleCount: integer; + i: integer; + Value: longint; + SampleCount: integer; SampleBuffer: PSmallIntArray; // buffer handled as array of samples - Boost: byte; + Boost: byte; begin // TODO: set boost per device case Ini.MicBoost of @@ -504,7 +499,6 @@ begin end; end; - { TAudioInputProcessor } constructor TAudioInputProcessor.Create; @@ -531,14 +525,14 @@ end; // See: TIni.LoadInputDeviceCfg() procedure TAudioInputProcessor.UpdateInputDeviceConfig; var - deviceIndex: integer; - newDevice: boolean; + deviceIndex: integer; + newDevice: boolean; deviceIniIndex: integer; - deviceCfg: PInputDeviceConfig; - device: TAudioInputDevice; - channelCount: integer; - channelIndex: integer; - i: integer; + deviceCfg: PInputDeviceConfig; + device: TAudioInputDevice; + channelCount: integer; + channelIndex: integer; + i: integer; begin // Input devices - append detected soundcards for deviceIndex := 0 to High(DeviceList) do @@ -608,22 +602,20 @@ end; * Length - number of bytes in Buffer * Input - Soundcard-Input used for capture *} -procedure TAudioInputProcessor.HandleMicrophoneData(Buffer: PChar; Size: Cardinal; InputDevice: TAudioInputDevice); +procedure TAudioInputProcessor.HandleMicrophoneData(Buffer: PByteArray; Size: integer; InputDevice: TAudioInputDevice); var - MultiChannelBuffer: PChar; // buffer handled as array of bytes (offset relative to channel) - SingleChannelBuffer: PChar; // temporary buffer for new samples per channel + MultiChannelBuffer: PByteArray; // buffer handled as array of bytes (offset relative to channel) + SingleChannelBuffer: PByteArray; // temporary buffer for new samples per channel SingleChannelBufferSize: integer; - ChannelIndex: integer; - CaptureChannel: TCaptureBuffer; - AudioFormat: TAudioFormatInfo; - SampleSize: integer; - SampleCount: integer; - SamplesPerChannel: integer; - i: integer; + ChannelIndex: integer; + CaptureChannel: TCaptureBuffer; + AudioFormat: TAudioFormatInfo; + SampleSize: integer; + SamplesPerChannel: integer; + i: integer; begin AudioFormat := InputDevice.AudioFormat; SampleSize := AudioSampleSize[AudioFormat.Format]; - SampleCount := Size div SampleSize; SamplesPerChannel := Size div AudioFormat.FrameSize; SingleChannelBufferSize := SamplesPerChannel * SampleSize; @@ -638,7 +630,7 @@ begin begin // set offset according to channel index MultiChannelBuffer := @Buffer[ChannelIndex * SampleSize]; - // seperate channel-data from interleaved multi-channel (e.g. stereo) data + // separate channel-data from interleaved multi-channel (e.g. stereo) data for i := 0 to SamplesPerChannel-1 do begin Move(MultiChannelBuffer[i*AudioFormat.FrameSize], @@ -652,7 +644,6 @@ begin FreeMem(SingleChannelBuffer); end; - { TAudioInputBase } function TAudioInputBase.FinalizeRecord: boolean; @@ -670,13 +661,13 @@ end; *} procedure TAudioInputBase.CaptureStart; var - S: integer; - DeviceIndex: integer; + S: integer; + DeviceIndex: integer; ChannelIndex: integer; - Device: TAudioInputDevice; - DeviceCfg: PInputDeviceConfig; - DeviceUsed: boolean; - Player: integer; + Device: TAudioInputDevice; + DeviceCfg: PInputDeviceConfig; + DeviceUsed: boolean; + Player: integer; begin if (Started) then CaptureStop(); @@ -728,10 +719,10 @@ end; *} procedure TAudioInputBase.CaptureStop; var - DeviceIndex: integer; + DeviceIndex: integer; ChannelIndex: integer; - Device: TAudioInputDevice; - DeviceCfg: PInputDeviceConfig; + Device: TAudioInputDevice; + DeviceCfg: PInputDeviceConfig; begin for DeviceIndex := 0 to High(AudioInputProcessor.DeviceList) do begin @@ -758,17 +749,18 @@ var var i: integer; begin - Result := False; + Result := false; // search devices with same description - For i := 0 to deviceIndex-1 do + for i := 0 to deviceIndex-1 do begin if (AudioInputProcessor.DeviceList[i].Name = name) then begin - Result := True; + Result := true; Break; end; end; end; + begin count := 1; result := name; @@ -783,6 +775,3 @@ begin end; end. - - - diff --git a/Lua/src/base/URingBuffer.pas b/Lua/src/base/URingBuffer.pas index 515d0efb..684c13ee 100644 --- a/Lua/src/base/URingBuffer.pas +++ b/Lua/src/base/URingBuffer.pas @@ -39,7 +39,7 @@ uses type TRingBuffer = class private - RingBuffer: PChar; + RingBuffer: PByteArray; BufferCount: integer; BufferSize: integer; WritePos: integer; @@ -47,8 +47,10 @@ type public constructor Create(Size: integer); destructor Destroy; override; - function Read(Buffer: PChar; Count: integer): integer; - function Write(Buffer: PChar; Count: integer): integer; + function Read(Buffer: PByteArray; Count: integer): integer; + function Write(Buffer: PByteArray; Count: integer): integer; + function Size(): integer; + function Available(): integer; procedure Flush(); end; @@ -71,7 +73,7 @@ begin FreeMem(RingBuffer); end; -function TRingBuffer.Read(Buffer: PChar; Count: integer): integer; +function TRingBuffer.Read(Buffer: PByteArray; Count: integer): integer; var PartCount: integer; begin @@ -106,7 +108,7 @@ begin Result := Count; end; -function TRingBuffer.Write(Buffer: PChar; Count: integer): integer; +function TRingBuffer.Write(Buffer: PByteArray; Count: integer): integer; var PartCount: integer; begin @@ -143,6 +145,16 @@ begin Result := Count; end; +function TRingBuffer.Available(): integer; +begin + Result := BufferCount; +end; + +function TRingBuffer.Size(): integer; +begin + Result := BufferSize; +end; + procedure TRingBuffer.Flush(); begin ReadPos := 0; @@ -150,4 +162,4 @@ begin BufferCount := 0; end; -end.
\ No newline at end of file +end. diff --git a/Lua/src/base/UServices.pas b/Lua/src/base/UServices.pas deleted file mode 100644 index 3783c543..00000000 --- a/Lua/src/base/UServices.pas +++ /dev/null @@ -1,384 +0,0 @@ -{* 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 UServices; - -interface - -{$IFDEF FPC} - {$MODE Delphi} -{$ENDIF} - -{$I switches.inc} - -uses - uPluginDefs, - SysUtils; -{********************* - TServiceManager - Class for saving, managing and calling of Services. - Saves all Services and their Procs -*********************} - -type - TServiceName = String[60]; - PServiceInfo = ^TServiceInfo; - TServiceInfo = record - Self: THandle; //Handle of this Service - Hash: Integer; //4 Bit Hash of the Services Name - Name: TServiceName; //Name of this Service - - Owner: Integer; //If < 0 [-(DLLMan Pluginindex + 1)]; 0 - undefined, On Error Full shutdown, If < 0 [ModuleIndex - 1] - - Next: PServiceInfo; //Pointer to the Next Service in teh list - - //Here is s/t tricky - //To avoid writing of Wrapping Functions to offer a Service from a Class - //We save a Normal Proc or a Method of a Class - Case isClass: boolean of - False: (Proc: TUS_Service); //Proc that will be called on Event - True: (ProcOfClass: TUS_Service_of_Object); - end; - - TServiceManager = class - private - //Managing Service List - FirstService: PServiceInfo; - LastService: PServiceInfo; - - //Some Speed improvement by caching the last 4 called Services - //Most of the time a Service is called multiple times - ServiceCache: Array[0..3] of PServiceInfo; - NextCacheItem: Byte; - - //Next Service added gets this Handle: - NextHandle: THandle; - public - Constructor Create; - - Function AddService(const ServiceName: PChar; const Proc: TUS_Service = nil; const ProcofClass: TUS_Service_of_Object = nil): THandle; - Function DelService(const hService: THandle): integer; - - Function CallService(const ServiceName: PChar; const wParam: TwParam; lParam: TlParam): integer; - - Function NametoHash(const ServiceName: TServiceName): Integer; - Function ServiceExists(const ServiceName: PChar): Integer; - end; - -var - ServiceManager: TServiceManager; - -implementation -uses - ULog, - UCore; - -//------------ -// Create - Creates Class and Set Standard Values -//------------ -Constructor TServiceManager.Create; -begin - inherited; - - FirstService := nil; - LastService := nil; - - ServiceCache[0] := nil; - ServiceCache[1] := nil; - ServiceCache[2] := nil; - ServiceCache[3] := nil; - - NextCacheItem := 0; - - NextHandle := 1; - - {$IFDEF DEBUG} - debugWriteln('ServiceManager: Succesful created!'); - {$ENDIF} -end; - -//------------ -// Function Creates a new Service and Returns the Services Handle, -// 0 on Failure. (Name already exists) -//------------ -Function TServiceManager.AddService(const ServiceName: PChar; const Proc: TUS_Service; const ProcofClass: TUS_Service_of_Object): THandle; -var - Cur: PServiceInfo; -begin - Result := 0; - - If (@Proc <> nil) or (@ProcOfClass <> nil) then - begin - If (ServiceExists(ServiceName) = 0) then - begin //There is a Proc and the Service does not already exist - //Ok Add it! - - //Get Memory - GetMem(Cur, SizeOf(TServiceInfo)); - - //Fill it with Data - Cur.Next := nil; - - If (@Proc = nil) then - begin //Use the ProcofClass Method - Cur.isClass := True; - Cur.ProcOfClass := ProcofClass; - end - else //Use the normal Proc - begin - Cur.isClass := False; - Cur.Proc := Proc; - end; - - Cur.Self := NextHandle; - //Zero Name - Cur.Name := #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0; - Cur.Name := String(ServiceName); - Cur.Hash := NametoHash(Cur.Name); - - //Add Owner to Service - Cur.Owner := Core.CurExecuted; - - //Add Service to the List - If (FirstService = nil) then - FirstService := Cur; - - If (LastService <> nil) then - LastService.Next := Cur; - - LastService := Cur; - - {$IFDEF DEBUG} - debugWriteln('ServiceManager: Service added: ''' + ServiceName + ''', Handle: ' + InttoStr(Cur.Self)); - {$ENDIF} - - //Inc Next Handle - Inc(NextHandle); - end - {$IFDEF DEBUG} - else debugWriteln('ServiceManager: Try to readd Service: ' + ServiceName); - {$ENDIF} - end; -end; - -//------------ -// Function Destroys a Service, 0 on success, not 0 on Failure -//------------ -Function TServiceManager.DelService(const hService: THandle): integer; -var - Last, Cur: PServiceInfo; - I: Integer; -begin - Result := -1; - - Last := nil; - Cur := FirstService; - - //Search for Service to Delete - While (Cur <> nil) do - begin - If (Cur.Self = hService) then - begin //Found Service => Delete it - - //Delete from List - If (Last = nil) then //Found first Service - FirstService := Cur.Next - Else //Service behind the first - Last.Next := Cur.Next; - - //IF this is the LastService, correct LastService - If (Cur = LastService) then - LastService := Last; - - //Search for Service in Cache and delete it if found - For I := 0 to High(ServiceCache) do - If (ServiceCache[I] = Cur) then - begin - ServiceCache[I] := nil; - end; - - {$IFDEF DEBUG} - debugWriteln('ServiceManager: Removed Service succesful: ' + Cur.Name); - {$ENDIF} - - //Free Memory - Freemem(Cur, SizeOf(TServiceInfo)); - - //Break the Loop - Break; - end; - - //Go to Next Service - Last := Cur; - Cur := Cur.Next; - end; -end; - -//------------ -// Function Calls a Services Proc -// Returns Services Return Value or SERVICE_NOT_FOUND on Failure -//------------ -Function TServiceManager.CallService(const ServiceName: PChar; const wParam: TwParam; lParam: TlParam): integer; -var - SExists: Integer; - Service: PServiceInfo; - CurExecutedBackup: Integer; //backup of Core.CurExecuted Attribute -begin - Result := SERVICE_NOT_FOUND; - SExists := ServiceExists(ServiceName); - If (SExists <> 0) then - begin - //Backup CurExecuted - CurExecutedBackup := Core.CurExecuted; - - Service := Pointer(SExists); - - If (Service.isClass) then - //Use Proc of Class - Result := Service.ProcOfClass(wParam, lParam) - Else - //Use normal Proc - Result := Service.Proc(wParam, lParam); - - //Restore CurExecuted - Core.CurExecuted := CurExecutedBackup; - end; - - {$IFDEF DEBUG} - debugWriteln('ServiceManager: Service ''' + ServiceName + ''' called. Result: ' + InttoStr(Result)); - {$ENDIF} -end; - -//------------ -// Generates the Hash for the given Name -//------------ -Function TServiceManager.NametoHash(const ServiceName: TServiceName): Integer; -// FIXME: check if the non-asm version is fast enough and use it by default if so -{$IF Defined(CPUX86_64)} -{$IFDEF FPC} - {$ASMMODE Intel} -{$ENDIF} -asm - { CL: Counter; RAX: Result; RDX: Current Memory Address } - Mov RCX, 14 - Mov RDX, ServiceName {Save Address of String that should be "Hashed"} - Mov RAX, [RDX] - @FoldLoop: ADD RDX, 4 {jump 4 Byte(32 Bit) to the next tile } - ADD RAX, [RDX] {Add the Value of the next 4 Byte of the String to the Hash} - LOOP @FoldLoop {Fold again if there are Chars Left} -end; -{$ELSEIF Defined(CPU386) or Defined(CPUI386)} -{$IFDEF FPC} - {$ASMMODE Intel} -{$ENDIF} -asm - { CL: Counter; EAX: Result; EDX: Current Memory Address } - Mov ECX, 14 {Init Counter, Fold 14 Times to get 4 Bytes out of 60} - Mov EDX, ServiceName {Save Address of String that should be "Hashed"} - Mov EAX, [EDX] - @FoldLoop: ADD EDX, 4 {jump 4 Byte(32 Bit) to the next tile } - ADD EAX, [EDX] {Add the Value of the next 4 Byte of the String to the Hash} - LOOP @FoldLoop {Fold again if there are Chars Left} -end; -{$ELSE} -var - i: integer; - ptr: ^integer; -begin - ptr := @ServiceName; - Result := 0; - for i := 1 to 14 do - begin - Result := Result + ptr^; - Inc(ptr); - end; -end; -{$IFEND} - - -//------------ -// Function Returns Non Zero if a Service with the given Name Exists, otherwise 0 -//------------ -Function TServiceManager.ServiceExists(const ServiceName: PChar): Integer; -var - Name: TServiceName; - Hash: Integer; - Cur: PServiceInfo; - I: Byte; -begin - Result := 0; - // to-do : Write a Metbod (in ASM) to Zero and Add in one turn (faster then this dirty hack ;) - //Zero Name: - Name := #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0 + #0; - //Add Service Name - Name := String(ServiceName); - Hash := NametoHash(Name); - - //First of all Look for the Service in Cache - For I := 0 to High(ServiceCache) do - begin - If (ServiceCache[I] <> nil) AND (ServiceCache[I].Hash = Hash) then - begin - If (ServiceCache[I].Name = Name) then - begin //Found Service in Cache - Result := Integer(ServiceCache[I]); - - {$IFDEF DEBUG} - debugWriteln('ServiceManager: Found Service in Cache: ''' + ServiceName + ''''); - {$ENDIF} - - Break; - end; - end; - end; - - If (Result = 0) then - begin - Cur := FirstService; - While (Cur <> nil) do - begin - If (Cur.Hash = Hash) then - begin - If (Cur.Name = Name) then - begin //Found the Service - Result := Integer(Cur); - - {$IFDEF DEBUG} - debugWriteln('ServiceManager: Found Service in List: ''' + ServiceName + ''''); - {$ENDIF} - - //Add to Cache - ServiceCache[NextCacheItem] := Cur; - NextCacheItem := (NextCacheItem + 1) AND 3; - Break; - end; - end; - - Cur := Cur.Next; - end; - end; -end; - -end. diff --git a/Lua/src/base/USingScores.pas b/Lua/src/base/USingScores.pas index 9ae48548..be0d4a58 100644 --- a/Lua/src/base/USingScores.pas +++ b/Lua/src/base/USingScores.pas @@ -34,212 +34,212 @@ interface {$I switches.inc} uses - UThemes, gl, + UThemes, UTexture; ////////////////////////////////////////////////////////////// // ATTENTION: // -// Enabled Flag does not Work atm. This should cause Popups // -// Not to Move and Scores to stay until Renenabling. // -// To use e.g. in Pause Mode // -// Also InVisible Flag causes Attributes not to change. // -// This should be fixed after next Draw when Visible = True,// -// but not testet yet // +// Enabled flag does not work atm. This should cause popups // +// not to move and scores to stay until re-enabling. // +// To use e.g. in pause mode // +// also invisible flag causes attributes not to change. // +// This should be fixed after next draw when visible = true,// +// but not tested yet // ////////////////////////////////////////////////////////////// -//Some constants containing options that could change by time +// some constants containing options that could change by time const - MaxPlayers = 6; //Maximum of Players that could be added - MaxPositions = 6; //Maximum of Score Positions that could be added + MaxPlayers = 6; // maximum of players that could be added + MaxPositions = 6; // maximum of score positions that could be added type //----------- - // TScorePlayer - Record Containing Information about a Players Score + // TScorePlayer - record containing information about a players score //----------- TScorePlayer = record - Position: Byte; //Index of the Position where the Player should be Drawn - Enabled: Boolean; //Is the Score Display Enabled - Visible: Boolean; //Is the Score Display Visible - Score: Word; //Current Score of the Player - ScoreDisplayed: Word; //Score cur. Displayed(for counting up) - ScoreBG: TTexture;//Texture of the Players Scores BG - Color: TRGB; //Teh Players Color - RBPos: Real; //Cur. Percentille of the Rating Bar - RBTarget: Real; //Target Position of Rating Bar - RBVisible:Boolean; //Is Rating bar Drawn + Position: byte; // index of the position where the player should be drawn + Enabled: boolean; // is the score display enabled + Visible: boolean; // is the score display visible + Score: word; // current score of the player + ScoreDisplayed: word; // score cur. displayed (for counting up) + ScoreBG: TTexture; // texture of the players scores bg + Color: TRGB; // the players color + RBPos: real; // cur. percentille of the rating bar + RBTarget: real; // target position of rating bar + RBVisible: boolean; // is rating bar drawn end; - aScorePlayer = array[0..MaxPlayers-1] of TScorePlayer; + aScorePlayer = array [0..MaxPlayers-1] of TScorePlayer; //----------- - // TScorePosition - Record Containing Information about a Score Position, that can be used + // TScorePosition - record containing information about a score position, that can be used //----------- PScorePosition = ^TScorePosition; TScorePosition = record - //The Position is Used for Which Playercount - PlayerCount: Byte; - // 1 - One Player per Screen - // 2 - 2 Players per Screen - // 4 - 3 Players per Screen - // 6 would be 2 and 3 Players per Screen - - BGX: Real; //X Position of the Score BG - BGY: Real; //Y Position of the Score BG - BGW: Real; //Width of the Score BG - BGH: Real; //Height of the Score BG - - RBX: Real; //X Position of the Rating Bar - RBY: Real; //Y Position of the Rating Bar - RBW: Real; //Width of the Rating Bar - RBH: Real; //Height of the Rating Bar - - TextX: Real; //X Position of the Score Text - TextY: Real; //Y Position of the Score Text - TextFont: Byte; //Font of the Score Text - TextSize: integer; //Size of the Score Text - - PUW: Real; //Width of the LineBonus Popup - PUH: Real; //Height of the LineBonus Popup - PUFont: Byte; //Font for the PopUps - PUFontSize: integer; //FontSize for the PopUps - PUStartX: Real; //X Start Position of the LineBonus Popup - PUStartY: Real; //Y Start Position of the LineBonus Popup - PUTargetX: Real; //X Target Position of the LineBonus Popup - PUTargetY: Real; //Y Target Position of the LineBonus Popup + // the position is used for which playercount + PlayerCount: byte; + // 1 - 1 player per screen + // 2 - 2 players per screen + // 4 - 3 players per screen + // 6 would be 2 and 3 players per screen + + BGX: real; // x position of the score bg + BGY: real; // y position of the score bg + BGW: real; // width of the score bg + BGH: real; // height of the score bg + + RBX: real; // x position of the rating bar + RBY: real; // y position of the rating bar + RBW: real; // width of the rating bar + RBH: real; // height of the rating bar + + TextX: real; // x position of the score text + TextY: real; // y position of the score text + TextFont: byte; // font of the score text + TextSize: integer; // size of the score text + + PUW: real; // width of the line bonus popup + PUH: real; // height of the line bonus popup + PUFont: byte; // font for the popups + PUFontSize: integer; // font size for the popups + PUStartX: real; // x start position of the line bonus popup + PUStartY: real; // y start position of the line bonus popup + PUTargetX: real; // x target position of the line bonus popup + PUTargetY: real; // y target position of the line bonus popup end; - aScorePosition = array[0..MaxPositions-1] of TScorePosition; + aScorePosition = array [0..MaxPositions-1] of TScorePosition; //----------- - // TScorePopUp - Record Containing Information about a LineBonus Popup - // List, Next Item is Saved in Next attribute + // TScorePopUp - record containing information about a line bonus popup + // list, next item is saved in next attribute //----------- PScorePopUp = ^TScorePopUp; TScorePopUp = record - Player: Byte; //Index of the PopUps Player - TimeStamp: Cardinal; //Timestamp of Popups Spawn - Rating: Byte; //0 to 8, Type of Rating (Cool, bad, etc.) - ScoreGiven:Word; //Score that has already been given to the Player - ScoreDiff: Word; //Difference Between Cur Score at Spawn and Old Score - Next: PScorePopUp; //Next Item in List + Player: byte; // index of the popups player + TimeStamp: cardinal; // timestamp of popups spawn + Rating: integer; // 0 to 8, type of rating (cool, bad, etc.) + ScoreGiven: integer; // score that has already been given to the player + ScoreDiff: integer; // difference between cur score at spawn and old score + Next: PScorePopUp; // next item in list end; aScorePopUp = array of TScorePopUp; //----------- - // TSingScores - Class containing Scores Positions and Drawing Scores, Rating Bar + Popups + // TSingScores - class containing scores positions and drawing scores, rating bar + popups //----------- TSingScores = class private aPositions: aScorePosition; - aPlayers: aScorePlayer; - oPositionCount: Byte; - oPlayerCount: Byte; + aPlayers: aScorePlayer; + oPositionCount: byte; + oPlayerCount: byte; - //Saves the First and Last Popup of the List + // saves the first and last popup of the list FirstPopUp: PScorePopUp; LastPopUp: PScorePopUp; - // Draws a Popup by Pointer + // draws a popup by pointer procedure DrawPopUp(const PopUp: PScorePopUp); - // Draws a Score by Playerindex - procedure DrawScore(const Index: Integer); + // draws a score by playerindex + procedure DrawScore(const Index: integer); - // Draws the RatingBar by Playerindex - procedure DrawRatingBar(const Index: Integer); + // draws the rating bar by playerindex + procedure DrawRatingBar(const Index: integer); - // Removes a PopUp w/o destroying the List + // removes a popup w/o destroying the list procedure KillPopUp(const last, cur: PScorePopUp); public - Settings: record //Record containing some Displaying Options - Phase1Time: Real; //time for Phase 1 to complete (in msecs) - //The Plop Up of the PopUp - Phase2Time: Real; //time for Phase 2 to complete (in msecs) - //The Moving (mainly Upwards) of the Popup - Phase3Time: Real; //time for Phase 3 to complete (in msecs) - //The Fade out and Score adding + Settings: record // Record containing some Displaying Options + Phase1Time: real; // time for phase 1 to complete (in msecs) + // the plop up of the popup + Phase2Time: real; // time for phase 2 to complete (in msecs) + // the moving (mainly upwards) of the popup + Phase3Time: real; // time for phase 3 to complete (in msecs) + // the fade out and score adding - PopUpTex: array [0..8] of TTexture; //Textures for every Popup Rating + PopUpTex: array [0..8] of TTexture; // textures for every popup rating - RatingBar_BG_Tex: TTexture; //Rating Bar Texs - RatingBar_FG_Tex: TTexture; - RatingBar_Bar_Tex: TTexture; + RatingBar_BG_Tex: TTexture; // rating bar texs + RatingBar_FG_Tex: TTexture; + RatingBar_Bar_Tex: TTexture; end; - Visible: Boolean; //Visibility of all Scores - Enabled: Boolean; //Scores are changed, PopUps are Moved etc. - RBVisible: Boolean; //Visibility of all Rating Bars + Visible: boolean; // visibility of all scores + Enabled: boolean; // scores are changed, popups are moved etc. + RBVisible: boolean; // visibility of all rating bars - //Propertys for Reading Position and Playercount - property PositionCount: Byte read oPositionCount; - property PlayerCount: Byte read oPlayerCount; - property Players: aScorePlayer read aPlayers; + // properties for reading position and playercount + property PositionCount: byte read oPositionCount; + property PlayerCount: byte read oPlayerCount; + property Players: aScorePlayer read aPlayers; property Positions: aScorePosition read aPositions; - //Constructor just sets some standard Settings + // constructor just sets some standard settings constructor Create; - // Adds a Position to Array and Increases Position Count + // adds a position to array and increases position count procedure AddPosition(const pPosition: PScorePosition); - // Adds a Player to Array and Increases Player Count - procedure AddPlayer(const ScoreBG: TTexture; const Color: TRGB; const Score: Word = 0; const Enabled: Boolean = True; const Visible: Boolean = True); + // adds a player to array and increases player count + procedure AddPlayer(const ScoreBG: TTexture; const Color: TRGB; const Score: word = 0; const Enabled: boolean = true; const Visible: boolean = true); - //Change a Players Visibility, Enable - procedure ChangePlayerVisibility(const Index: Byte; const pVisible: Boolean); - procedure ChangePlayerEnabled(const Index: Byte; const pEnabled: Boolean); + // change a players visibility, enable + procedure ChangePlayerVisibility(const Index: byte; const pVisible: boolean); + procedure ChangePlayerEnabled(const Index: byte; const pEnabled: boolean); - // Deletes all Player Information + // deletes all player information procedure ClearPlayers; - // Deletes Positions and Playerinformation + // deletes positions and playerinformation procedure Clear; - // Loads some Settings and the Positions from Theme + // loads some settings and the positions from theme procedure LoadfromTheme; - // has to be called after Positions and Players have been added, before first call of Draw - //It gives every Player a Score Position + // has to be called after positions and players have been added, before first call of draw + // it gives every player a score position procedure Init; - //Spawns a new Line Bonus PopUp for the Player - procedure SpawnPopUp(const PlayerIndex: Byte; const Rating: Byte; const Score: Word); + // spawns a new line bonus popup for the player + procedure SpawnPopUp(const PlayerIndex: byte; const Rating: integer; const Score: integer); - //Removes all PopUps from Mem + // removes all popups from mem procedure KillAllPopUps; - // Draws Scores and Linebonus PopUps + // draws scores and line bonus popups procedure Draw; end; - implementation -uses SDL, - SysUtils, - ULog, - UGraphic, - TextGL; +uses + SysUtils, + SDL, + TextGL, + ULog, + UGraphic; {** - * Sets some standard Settings + * sets some standard settings *} -Constructor TSingScores.Create; +constructor TSingScores.Create; begin inherited; - //Clear PopupList Pointers + // clear popuplist pointers FirstPopUp := nil; LastPopUp := nil; - //Clear Variables - Visible := True; - Enabled := True; - RBVisible := True; + // clear variables + Visible := true; + Enabled := true; + RBVisible := true; - //Clear Position Index - oPositionCount := 0; - oPlayerCount := 0; + // clear position index + oPositionCount := 0; + oPlayerCount := 0; Settings.Phase1Time := 350; // plop it up . -> [ ] Settings.Phase2Time := 550; // shift it up ^[ ]^ @@ -261,22 +261,21 @@ begin end; {** - * Adds a Position to Array and Increases Position Count + * adds a position to array and increases position count *} -Procedure TSingScores.AddPosition(const pPosition: PScorePosition); +procedure TSingScores.AddPosition(const pPosition: PScorePosition); begin if (PositionCount < MaxPositions) then begin aPositions[PositionCount] := pPosition^; - Inc(oPositionCount); end; end; {** - * Adds a Player to Array and Increases Player Count + * adds a player to array and increases player count *} -Procedure TSingScores.AddPlayer(const ScoreBG: TTexture; const Color: TRGB; const Score: Word; const Enabled: Boolean; const Visible: Boolean); +procedure TSingScores.AddPlayer(const ScoreBG: TTexture; const Color: TRGB; const Score: word; const Enabled: boolean; const Visible: boolean); begin if (PlayerCount < MaxPlayers) then begin @@ -284,48 +283,48 @@ begin aPlayers[PlayerCount].Enabled := Enabled; aPlayers[PlayerCount].Visible := Visible; aPlayers[PlayerCount].Score := Score; - aPlayers[PlayerCount].ScoreDisplayed := Score; + aPlayers[PlayerCount].ScoreDisplayed := Score; aPlayers[PlayerCount].ScoreBG := ScoreBG; aPlayers[PlayerCount].Color := Color; aPlayers[PlayerCount].RBPos := 0.5; aPlayers[PlayerCount].RBTarget := 0.5; - aPlayers[PlayerCount].RBVisible := True; + aPlayers[PlayerCount].RBVisible := true; Inc(oPlayerCount); end; end; {** - * Change a Players Visibility + * change a players visibility *} -Procedure TSingScores.ChangePlayerVisibility(const Index: Byte; const pVisible: Boolean); +procedure TSingScores.ChangePlayerVisibility(const Index: byte; const pVisible: boolean); begin if (Index < MaxPlayers) then aPlayers[Index].Visible := pVisible; end; {** - * Change Player Enabled + * change player enabled *} -Procedure TSingScores.ChangePlayerEnabled(const Index: Byte; const pEnabled: Boolean); +procedure TSingScores.ChangePlayerEnabled(const Index: byte; const pEnabled: boolean); begin if (Index < MaxPlayers) then aPlayers[Index].Enabled := pEnabled; end; {** - * Procedure Deletes all Player Information + * procedure deletes all player information *} -Procedure TSingScores.ClearPlayers; +procedure TSingScores.ClearPlayers; begin KillAllPopUps; oPlayerCount := 0; end; {** - * Procedure Deletes Positions and Playerinformation + * procedure deletes positions and playerinformation *} -Procedure TSingScores.Clear; +procedure TSingScores.Clear; begin KillAllPopUps; oPlayerCount := 0; @@ -333,14 +332,16 @@ begin end; {** - * Procedure Loads some Settings and the Positions from Theme + * procedure loads some settings and the positions from theme *} -Procedure TSingScores.LoadfromTheme; -var I: Integer; - Procedure AddbyStatics(const PC: Byte; const ScoreStatic, SingBarStatic: TThemeStatic; ScoreText: TThemeText); - var nPosition: TScorePosition; +procedure TSingScores.LoadfromTheme; +var + I: integer; + procedure AddbyStatics(const PC: byte; const ScoreStatic, SingBarStatic: TThemeStatic; ScoreText: TThemeText); + var + nPosition: TScorePosition; begin - nPosition.PlayerCount := PC; //Only for one Player Playing + nPosition.PlayerCount := PC; // only for one player playing nPosition.BGX := ScoreStatic.X; nPosition.BGY := ScoreStatic.Y; @@ -374,54 +375,57 @@ var I: Integer; begin Clear; - //Set Textures - //Popup Tex - For I := 0 to 8 do + // set textures + // popup tex + for I := 0 to 8 do Settings.PopUpTex[I] := Tex_SingLineBonusBack[I]; - //Rating Bar Tex + // rating bar tex Settings.RatingBar_BG_Tex := Tex_SingBar_Back; Settings.RatingBar_FG_Tex := Tex_SingBar_Front; Settings.RatingBar_Bar_Tex := Tex_SingBar_Bar; - //Load Positions from Theme + // load positions from theme - // Player1: + // player 1: AddByStatics(1, Theme.Sing.StaticP1ScoreBG, Theme.Sing.StaticP1SingBar, Theme.Sing.TextP1Score); AddByStatics(2, Theme.Sing.StaticP1TwoPScoreBG, Theme.Sing.StaticP1TwoPSingBar, Theme.Sing.TextP1TwoPScore); AddByStatics(4, Theme.Sing.StaticP1ThreePScoreBG, Theme.Sing.StaticP1ThreePSingBar, Theme.Sing.TextP1ThreePScore); - // Player2: + // player 2: AddByStatics(2, Theme.Sing.StaticP2RScoreBG, Theme.Sing.StaticP2RSingBar, Theme.Sing.TextP2RScore); AddByStatics(4, Theme.Sing.StaticP2MScoreBG, Theme.Sing.StaticP2MSingBar, Theme.Sing.TextP2MScore); - // Player3: + // player 3: AddByStatics(4, Theme.Sing.StaticP3RScoreBG, Theme.Sing.StaticP3SingBar, Theme.Sing.TextP3RScore); end; {** - * Spawns a new Line Bonus PopUp for the Player + * spawns a new line bonus popup for the player *} -Procedure TSingScores.SpawnPopUp(const PlayerIndex: Byte; const Rating: Byte; const Score: Word); -var Cur: PScorePopUp; +procedure TSingScores.SpawnPopUp(const PlayerIndex: byte; const Rating: integer; const Score: integer); +var + Cur: PScorePopUp; begin if (PlayerIndex < PlayerCount) then begin - //Get Memory and Add Data + // get memory and add data GetMem(Cur, SizeOf(TScorePopUp)); - Cur.Player := PlayerIndex; + Cur.Player := PlayerIndex; Cur.TimeStamp := SDL_GetTicks; - //limit rating value to 8 - //a higher value would cause a crash when selecting the bg textur + // limit rating value to 0..8 + // a higher value would cause a crash when selecting the bg texture if (Rating > 8) then Cur.Rating := 8 + else if (Rating < 0) then + Cur.Rating := 0 else Cur.Rating := Rating; Cur.ScoreGiven:= 0; - If (Players[PlayerIndex].Score < Score) then + if (Players[PlayerIndex].Score < Score) then begin Cur.ScoreDiff := Score - Players[PlayerIndex].Score; aPlayers[PlayerIndex].Score := Score; @@ -430,77 +434,77 @@ begin Cur.ScoreDiff := 0; Cur.Next := nil; - //Log.LogError('TSingScores.SpawnPopUp| Player: ' + InttoStr(PlayerIndex) + ', Score: ' + InttoStr(Score) + ', ScoreDiff: ' + InttoStr(Cur.ScoreDiff)); + // Log.LogError('TSingScores.SpawnPopUp| Player: ' + InttoStr(PlayerIndex) + ', Score: ' + InttoStr(Score) + ', ScoreDiff: ' + InttoStr(Cur.ScoreDiff)); - //Add it to the Chain + // add it to the chain if (FirstPopUp = nil) then - //the first PopUp in the List + // the first popup in the list FirstPopUp := Cur else - //second or earlier popup + // second or earlier popup LastPopUp.Next := Cur; - //Set new Popup to Last PopUp in the List + // set new popup to last popup in the list LastPopUp := Cur; end else - Log.LogError('TSingScores: Try to add PopUp for not existing player'); + Log.LogError('TSingScores: Try to add popup for non-existing player'); end; {** - * Removes a PopUp w/o destroying the List + * removes a popup w/o destroying the list *} -Procedure TSingScores.KillPopUp(const last, cur: PScorePopUp); +procedure TSingScores.KillPopUp(const last, cur: PScorePopUp); begin - //Give Player the Last Points that missing till now + // give player the last points that missing till now aPlayers[Cur.Player].ScoreDisplayed := aPlayers[Cur.Player].ScoreDisplayed + Cur.ScoreDiff - Cur.ScoreGiven; - //Change Bars Position + // change bars position if (Cur.ScoreDiff > 0) THEN - begin //Popup w/ scorechange -> give missing Percentille + begin // popup w/ scorechange -> give missing percentille aPlayers[Cur.Player].RBTarget := aPlayers[Cur.Player].RBTarget + (Cur.ScoreDiff - Cur.ScoreGiven) / Cur.ScoreDiff * (Cur.Rating / 20 - 0.26); end else - begin //Popup w/o scorechange -> give complete Percentille + begin // popup w/o scorechange -> give complete percentille aPlayers[Cur.Player].RBTarget := aPlayers[Cur.Player].RBTarget + (Cur.Rating / 20 - 0.26); end; - If (aPlayers[Cur.Player].RBTarget > 1) then + if (aPlayers[Cur.Player].RBTarget > 1) then aPlayers[Cur.Player].RBTarget := 1 else - If (aPlayers[Cur.Player].RBTarget < 0) then + if (aPlayers[Cur.Player].RBTarget < 0) then aPlayers[Cur.Player].RBTarget := 0; - //If this is the First PopUp => Make Next PopUp the First - If (Cur = FirstPopUp) then + // if this is the first popup => make next popup the first + if (Cur = FirstPopUp) then FirstPopUp := Cur.Next - //Else => Remove Curent Popup from Chain + // else => remove curent popup from chain else Last.Next := Cur.Next; - //If this is the Last PopUp, Make PopUp before the Last - If (Cur = LastPopUp) then + // if this is the last popup, make popup before the last + if (Cur = LastPopUp) then LastPopUp := Last; - //Free the Memory + // free the memory FreeMem(Cur, SizeOf(TScorePopUp)); end; {** - * Removes all PopUps from Mem + * removes all popups from mem *} -Procedure TSingScores.KillAllPopUps; +procedure TSingScores.KillAllPopUps; var Cur: PScorePopUp; Last: PScorePopUp; begin Cur := FirstPopUp; - //Remove all PopUps: - While (Cur <> nil) do + // remove all popups: + while (Cur <> nil) do begin Last := Cur; Cur := Cur.Next; @@ -512,40 +516,42 @@ begin end; {** - * Has to be called after Positions and Players have been added, before first call of Draw - * It gives every Player a Score Position + * has to be called after positions and players have been added, before first call of draw + * it gives each player a score position *} -Procedure TSingScores.Init; +procedure TSingScores.Init; var - PlC: Array [0..1] of Byte; //Playercount First Screen and Second Screen - I, J: Integer; - MaxPlayersperScreen: Byte; - CurPlayer: Byte; - - Function GetPositionCountbyPlayerCount(bPlayerCount: Byte): Byte; - var I: Integer; + PlC: array [0..1] of byte; // playercount first screen and second screen + I, J: integer; + MaxPlayersperScreen: byte; + CurPlayer: byte; + + function GetPositionCountbyPlayerCount(bPlayerCount: byte): byte; + var + I: integer; begin Result := 0; bPlayerCount := 1 shl (bPlayerCount - 1); - For I := 0 to PositionCount-1 do + for I := 0 to PositionCount - 1 do begin - If ((aPositions[I].PlayerCount AND bPlayerCount) <> 0) then + if ((aPositions[I].PlayerCount and bPlayerCount) <> 0) then Inc(Result); end; end; - Function GetPositionbyPlayernum(bPlayerCount, bPlayer: Byte): Byte; - var I: Integer; + function GetPositionbyPlayernum(bPlayerCount, bPlayer: byte): byte; + var + I: integer; begin bPlayerCount := 1 shl (bPlayerCount - 1); - Result := High(Byte); + Result := High(byte); - For I := 0 to PositionCount-1 do + for I := 0 to PositionCount - 1 do begin - If ((aPositions[I].PlayerCount AND bPlayerCount) <> 0) then + if ((aPositions[I].PlayerCount and bPlayerCount) <> 0) then begin - If (bPlayer = 0) then + if (bPlayer = 0) then begin Result := I; Break; @@ -559,17 +565,16 @@ var begin MaxPlayersPerScreen := 0; - For I := 1 to 6 do + for I := 1 to 6 do begin - //If there are enough Positions -> Write to MaxPlayers - If (GetPositionCountbyPlayerCount(I) = I) then + // if there are enough positions -> write to maxplayers + if (GetPositionCountbyPlayerCount(I) = I) then MaxPlayersPerScreen := I else Break; end; - - //Split Players to both Screen or Display on One Screen + // split players to both screens or display on one screen if (Screens = 2) and (MaxPlayersPerScreen < PlayerCount) then begin PlC[0] := PlayerCount div 2 + PlayerCount mod 2; @@ -581,9 +586,8 @@ begin PlC[1] := 0; end; - - //Check if there are enough Positions for all Players - For I := 0 to Screens - 1 do + // check if there are enough positions for all players + for I := 0 to Screens - 1 do begin if (PlC[I] > MaxPlayersperScreen) then begin @@ -593,34 +597,34 @@ begin end; CurPlayer := 0; - //Give every Player a Position - For I := 0 to Screens - 1 do - For J := 0 to PlC[I]-1 do + // give every player a position + for I := 0 to Screens - 1 do + for J := 0 to PlC[I]-1 do begin - aPlayers[CurPlayer].Position := GetPositionbyPlayernum(PlC[I], J) OR (I shl 7); - //Log.LogError('Player ' + InttoStr(CurPlayer) + ' gets Position: ' + InttoStr(aPlayers[CurPlayer].Position)); + aPlayers[CurPlayer].Position := GetPositionbyPlayernum(PlC[I], J) or (I shl 7); + // Log.LogError('Player ' + InttoStr(CurPlayer) + ' gets Position: ' + InttoStr(aPlayers[CurPlayer].Position)); Inc(CurPlayer); end; end; {** - * Draws Scores and Linebonus PopUps + * draws scores and linebonus popups *} -Procedure TSingScores.Draw; +procedure TSingScores.Draw; var - I: Integer; - CurTime: Cardinal; + I: integer; + CurTime: cardinal; CurPopUp, LastPopUp: PScorePopUp; begin CurTime := SDL_GetTicks; - If Visible then + if Visible then begin - //Draw Popups + // draw popups LastPopUp := nil; CurPopUp := FirstPopUp; - While (CurPopUp <> nil) do + while (CurPopUp <> nil) do begin if (CurTime - CurPopUp.TimeStamp > Settings.Phase1Time + Settings.Phase2Time + Settings.Phase3Time) then begin @@ -639,64 +643,64 @@ begin end; - IF (RBVisible) then - //Draw Players w/ Rating Bar - For I := 0 to PlayerCount-1 do + if (RBVisible) then + // draw players w/ rating bar + for I := 0 to PlayerCount-1 do begin DrawScore(I); DrawRatingBar(I); end else - //Draw Players w/o Rating Bar - For I := 0 to PlayerCount-1 do + // draw players w/o rating bar + for I := 0 to PlayerCount-1 do begin DrawScore(I); end; - end; //eo Visible + end; // eo visible end; {** - * Draws a Popup by Pointer + * draws a popup by pointer *} -Procedure TSingScores.DrawPopUp(const PopUp: PScorePopUp); +procedure TSingScores.DrawPopUp(const PopUp: PScorePopUp); var - Progress: Real; - CurTime: Cardinal; - X, Y, W, H, Alpha: Real; - FontSize: integer; - FontOffset: Real; - TimeDiff: Cardinal; - PIndex: Byte; - TextLen: Real; - ScoretoAdd: Word; - PosDiff: Real; + Progress: real; + CurTime: cardinal; + X, Y, W, H, Alpha: real; + FontSize: integer; + FontOffset: real; + TimeDiff: cardinal; + PIndex: byte; + TextLen: real; + ScoretoAdd: word; + PosDiff: real; begin if (PopUp <> nil) then begin - //Only Draw if Player has a Position + // only draw if player has a position PIndex := Players[PopUp.Player].Position; - If PIndex <> high(byte) then + if PIndex <> High(byte) then begin - //Only Draw if Player is on Cur Screen - If ((Players[PopUp.Player].Position AND 128) = 0) = (ScreenAct = 1) then + // only draw if player is on cur screen + if ((Players[PopUp.Player].Position and 128) = 0) = (ScreenAct = 1) then begin CurTime := SDL_GetTicks; - If Not (Enabled AND Players[PopUp.Player].Enabled) then - //Increase Timestamp with TIem where there is no Movement ... + if not (Enabled and Players[PopUp.Player].Enabled) then + // increase timestamp with tiem where there is no movement ... begin - //Inc(PopUp.TimeStamp, LastRender); + // Inc(PopUp.TimeStamp, LastRender); end; TimeDiff := CurTime - PopUp.TimeStamp; - //Get Position of PopUp - PIndex := PIndex AND 127; + // get position of popup + PIndex := PIndex and 127; - //Check for Phase ... - If (TimeDiff <= Settings.Phase1Time) then + // check for phase ... + if (TimeDiff <= Settings.Phase1Time) then begin - //Phase 1 - The Ploping up + // phase 1 - the ploping up Progress := TimeDiff / Settings.Phase1Time; @@ -707,25 +711,25 @@ begin Y := aPositions[PIndex].PUStartY + (aPositions[PIndex].PUH - H)/2; FontSize := Round(Progress * aPositions[PIndex].PUFontSize); - FontOffset := (H - FontSize) / 2; + FontOffset := (H - FontSize) / 2; Alpha := 1; end - Else If (TimeDiff <= Settings.Phase2Time + Settings.Phase1Time) then + else if (TimeDiff <= Settings.Phase2Time + Settings.Phase1Time) then begin - //Phase 2 - The Moving + // phase 2 - the moving Progress := (TimeDiff - Settings.Phase1Time) / Settings.Phase2Time; W := aPositions[PIndex].PUW; H := aPositions[PIndex].PUH; PosDiff := aPositions[PIndex].PUTargetX - aPositions[PIndex].PUStartX; - If PosDiff > 0 then + if PosDiff > 0 then PosDiff := PosDiff + W; X := aPositions[PIndex].PUStartX + PosDiff * sqr(Progress); PosDiff := aPositions[PIndex].PUTargetY - aPositions[PIndex].PUStartY; - If PosDiff < 0 then + if PosDiff < 0 then PosDiff := PosDiff + aPositions[PIndex].BGH; Y := aPositions[PIndex].PUStartY + PosDiff * sqr(Progress); @@ -736,65 +740,68 @@ begin else begin - //Phase 3 - The Fading out + Score adding + // phase 3 - the fading out + score adding Progress := (TimeDiff - Settings.Phase1Time - Settings.Phase2Time) / Settings.Phase3Time; - If (PopUp.Rating > 0) then + if (PopUp.Rating > 0) then begin - //Add Scores if Player Enabled - If (Enabled AND Players[PopUp.Player].Enabled) then + // add scores if player enabled + if (Enabled and Players[PopUp.Player].Enabled) then begin ScoreToAdd := Round(PopUp.ScoreDiff * Progress) - PopUp.ScoreGiven; Inc(PopUp.ScoreGiven, ScoreToAdd); aPlayers[PopUp.Player].ScoreDisplayed := Players[PopUp.Player].ScoreDisplayed + ScoreToAdd; - //Change Bars Position - aPlayers[PopUp.Player].RBTarget := aPlayers[PopUp.Player].RBTarget + ScoreToAdd/PopUp.ScoreDiff * (PopUp.Rating / 20 - 0.26); - If (aPlayers[PopUp.Player].RBTarget > 1) then + // change bar positions + if PopUp.ScoreDiff = 0 then + Log.LogError('TSingScores.DrawPopUp', 'PopUp.ScoreDiff is 0 and we want to divide by it. No idea how this happens.') + else + aPlayers[PopUp.Player].RBTarget := aPlayers[PopUp.Player].RBTarget + ScoreToAdd/PopUp.ScoreDiff * (PopUp.Rating / 20 - 0.26); + if (aPlayers[PopUp.Player].RBTarget > 1) then aPlayers[PopUp.Player].RBTarget := 1 - else If (aPlayers[PopUp.Player].RBTarget < 0) then + else if (aPlayers[PopUp.Player].RBTarget < 0) then aPlayers[PopUp.Player].RBTarget := 0; end; - //Set Positions etc. - Alpha := 0.7 - 0.7 * Progress; + // set positions etc. + Alpha := 0.7 - 0.7 * Progress; W := aPositions[PIndex].PUW; H := aPositions[PIndex].PUH; PosDiff := aPositions[PIndex].PUTargetX - aPositions[PIndex].PUStartX; - If (PosDiff > 0) then + if (PosDiff > 0) then PosDiff := W else PosDiff := 0; X := aPositions[PIndex].PUTargetX + PosDiff * Progress; PosDiff := aPositions[PIndex].PUTargetY - aPositions[PIndex].PUStartY; - If (PosDiff < 0) then + if (PosDiff < 0) then PosDiff := -aPositions[PIndex].BGH else PosDiff := 0; - Y := aPositions[PIndex].PUTargetY - PosDiff * (1-Progress); + Y := aPositions[PIndex].PUTargetY - PosDiff * (1 - Progress); FontSize := aPositions[PIndex].PUFontSize; FontOffset := (H - FontSize) / 2; end else begin - //Here the Effect that Should be shown if a PopUp without Score is Drawn - //And or Spawn with the GraphicObjects etc. - //Some Work for Blindy to do :P + // here the effect that should be shown if a popup without score is drawn + // and or spawn with the graphicobjects etc. + // some work for blindy to do :p - //ATM: Just Let it Slide in the Scores just like the Normal PopUp + // atm: just let it slide in the scores just like the normal popup Alpha := 0; end; end; - //Draw PopUp + // draw popup - if (Alpha > 0) AND (Players[PopUp.Player].Visible) then + if (Alpha > 0) and (Players[PopUp.Player].Visible) then begin - //Draw BG: + // draw bg: glEnable(GL_TEXTURE_2D); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); @@ -812,45 +819,46 @@ begin glDisable(GL_TEXTURE_2D); glDisable(GL_BLEND); - //Set FontStyle and Size + // set font style and size SetFontStyle(aPositions[PIndex].PUFont); - SetFontItalic(False); + SetFontItalic(false); SetFontSize(FontSize); + SetFontReflection(false, 0); - //Draw Text + // draw text TextLen := glTextWidth(Theme.Sing.LineBonusText[PopUp.Rating]); - //Color and Pos + // color and pos SetFontPos (X + (W - TextLen) / 2, Y + FontOffset); glColor4f(1, 1, 1, Alpha); - //Draw + // draw glPrint(Theme.Sing.LineBonusText[PopUp.Rating]); - end; //eo Alpha check - end; //eo Right Screen - end; //eo Player has Position + end; // eo alpha check + end; // eo right screen + end; // eo player has position end else - Log.LogError('TSingScores: Try to Draw a not existing PopUp'); + Log.LogError('TSingScores: Try to draw a non-existing popup'); end; {** - * Draws a Score by Playerindex + * draws a score by playerindex *} -Procedure TSingScores.DrawScore(const Index: Integer); +procedure TSingScores.DrawScore(const Index: integer); var Position: PScorePosition; ScoreStr: String; begin - //Only Draw if Player has a Position - If Players[Index].Position <> high(byte) then + // only draw if player has a position + if Players[Index].Position <> High(byte) then begin - //Only Draw if Player is on Cur Screen - If (((Players[Index].Position AND 128) = 0) = (ScreenAct = 1)) AND Players[Index].Visible then + // only draw if player is on cur screen + if (((Players[Index].Position and 128) = 0) = (ScreenAct = 1)) and Players[Index].Visible then begin Position := @aPositions[Players[Index].Position and 127]; - //Draw ScoreBG + // draw scorebg glEnable(GL_TEXTURE_2D); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); @@ -868,50 +876,51 @@ begin glDisable(GL_TEXTURE_2D); glDisable(GL_BLEND); - //Draw Score Text + // draw score text SetFontStyle(Position.TextFont); - SetFontItalic(False); + SetFontItalic(false); SetFontSize(Position.TextSize); SetFontPos(Position.TextX, Position.TextY); + SetFontReflection(false, 0); ScoreStr := InttoStr(Players[Index].ScoreDisplayed div 10) + '0'; - While (Length(ScoreStr) < 5) do + while (Length(ScoreStr) < 5) do ScoreStr := '0' + ScoreStr; glPrint(ScoreStr); - end; //eo Right Screen - end; //eo Player has Position + end; // eo right screen + end; // eo player has position end; -Procedure TSingScores.DrawRatingBar(const Index: Integer); +procedure TSingScores.DrawRatingBar(const Index: integer); var - Position: PScorePosition; - R,G,B, Size: Real; - Diff: Real; + Position: PScorePosition; + R, G, B: real; + Size, Diff: real; begin - //Only Draw if Player has a Position - if Players[Index].Position <> high(byte) then + // only draw if player has a position + if Players[Index].Position <> High(byte) then begin - //Only Draw if Player is on Cur Screen + // only draw if player is on cur screen if (((Players[Index].Position and 128) = 0) = (ScreenAct = 1) and Players[index].RBVisible and Players[index].Visible) then begin Position := @aPositions[Players[Index].Position and 127]; - if (Enabled AND Players[Index].Enabled) then + if (Enabled and Players[Index].Enabled) then begin - //Move Position if Enabled + // move position if enabled Diff := Players[Index].RBTarget - Players[Index].RBPos; - If(Abs(Diff) < 0.02) then + if (Abs(Diff) < 0.02) then aPlayers[Index].RBPos := aPlayers[Index].RBTarget else aPlayers[Index].RBPos := aPlayers[Index].RBPos + Diff*0.1; end; - //Get Colors for RatingBar + // get colors for rating bar if (Players[index].RBPos <= 0.22) then begin R := 1; @@ -921,7 +930,7 @@ begin else if (Players[index].RBPos <= 0.42) then begin R := 1; - G := Players[index].RBPos*5; + G := Players[index].RBPos * 5; B := 0; end else if (Players[index].RBPos <= 0.57) then @@ -932,7 +941,7 @@ begin end else if (Players[index].RBPos <= 0.77) then begin - R := 1-(Players[index].RBPos-0.57)*5; + R := 1 - (Players[index].RBPos - 0.57) * 5; G := 1; B := 0; end @@ -943,12 +952,12 @@ begin B := 0; end; - //Enable all glFuncs Needed + // enable all glfuncs needed glEnable(GL_TEXTURE_2D); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - //Draw RatingBar BG + // draw rating bar bg glColor4f(1, 1, 1, 0.8); glBindTexture(GL_TEXTURE_2D, Settings.RatingBar_BG_Tex.TexNum); @@ -966,7 +975,7 @@ begin glVertex2f(Position.RBX+Position.RBW, Position.RBY); glEnd; - //Draw Rating bar itself + // draw rating bar itself Size := Position.RBX + Position.RBW * Players[Index].RBPos; glColor4f(R, G, B, 1); glBindTexture(GL_TEXTURE_2D, Settings.RatingBar_Bar_Tex.TexNum); @@ -984,7 +993,7 @@ begin glVertex2f(Size, Position.RBY); glEnd; - //Draw Ratingbar FG (Teh thing with the 3 lines to get better readability) + // draw rating bar fg (the thing with the 3 lines to get better readability) glColor4f(1, 1, 1, 0.6); glBindTexture(GL_TEXTURE_2D, Settings.RatingBar_FG_Tex.TexNum); glBegin(GL_QUADS); @@ -1001,11 +1010,11 @@ begin glVertex2f(Position.RBX + Position.RBW, Position.RBY); glEnd; - //Disable all Enabled glFuncs + // disable all enabled glfuncs glDisable(GL_TEXTURE_2D); glDisable(GL_BLEND); - end; //eo Right Screen - end; //eo Player has Position + end; // eo Right Screen + end; // eo Player has Position end; end. diff --git a/Lua/src/base/USkins.pas b/Lua/src/base/USkins.pas index 59c590e5..6ef5c596 100644 --- a/Lua/src/base/USkins.pas +++ b/Lua/src/base/USkins.pas @@ -33,46 +33,52 @@ interface {$I switches.inc} +uses + UPath; + type TSkinTexture = record - Name: string; - FileName: string; + Name: string; + FileName: IPath; end; TSkinEntry = record - Theme: string; - Name: string; - Path: string; - FileName: string; - Creator: string; // not used yet + Theme: string; + Name: string; + Path: IPath; + FileName: IPath; + Creator: string; // not used yet end; TSkin = class - Skin: array of TSkinEntry; - SkinTexture: array of TSkinTexture; - SkinPath: string; - Color: integer; + Skin: array of TSkinEntry; + SkinTexture: array of TSkinTexture; + SkinPath: IPath; + Color: integer; constructor Create; procedure LoadList; - procedure ParseDir(Dir: string); - procedure LoadHeader(FileName: string); + procedure ParseDir(Dir: IPath); + procedure LoadHeader(FileName: IPath); procedure LoadSkin(Name: string); - function GetTextureFileName(TextureName: string): string; + function GetTextureFileName(TextureName: string): IPath; function GetSkinNumber(Name: string): integer; procedure onThemeChange; end; var - Skin: TSkin; + Skin: TSkin; implementation -uses IniFiles, - Classes, - SysUtils, - UMain, - ULog, - UIni; +uses + IniFiles, + Classes, + SysUtils, + UIni, + ULog, + UMain, + UPathUtils, + UFileSystem; constructor TSkin.Create; begin @@ -84,43 +90,43 @@ end; procedure TSkin.LoadList; var - SR: TSearchRec; + Iter: IFileIterator; + DirInfo: TFileInfo; begin - if FindFirst(SkinsPath+'*', faDirectory, SR) = 0 then begin - repeat - if (SR.Name <> '.') and (SR.Name <> '..') then - ParseDir(SkinsPath + SR.Name + PathDelim); - until FindNext(SR) <> 0; - end; // if - FindClose(SR); + Iter := FileSystem.FileFind(SkinsPath.Append('*'), faDirectory); + while Iter.HasNext do + begin + DirInfo := Iter.Next(); + if (not DirInfo.Name.Equals('.')) and (not DirInfo.Name.Equals('..')) then + ParseDir(SkinsPath.Append(DirInfo.Name, pdAppend)); + end; end; -procedure TSkin.ParseDir(Dir: string); +procedure TSkin.ParseDir(Dir: IPath); var - SR: TSearchRec; + Iter: IFileIterator; + IniInfo: TFileInfo; begin - if FindFirst(Dir + '*.ini', faAnyFile, SR) = 0 then begin - repeat - - if (SR.Name <> '.') and (SR.Name <> '..') then - LoadHeader(Dir + SR.Name); - - until FindNext(SR) <> 0; + Iter := FileSystem.FileFind(Dir.Append('*.ini'), 0); + while Iter.HasNext do + begin + IniInfo := Iter.Next; + LoadHeader(Dir.Append(IniInfo.Name)); end; end; -procedure TSkin.LoadHeader(FileName: string); +procedure TSkin.LoadHeader(FileName: IPath); var - SkinIni: TMemIniFile; - S: integer; + SkinIni: TMemIniFile; + S: integer; begin - SkinIni := TMemIniFile.Create(FileName); + SkinIni := TMemIniFile.Create(FileName.ToNative); S := Length(Skin); SetLength(Skin, S+1); - Skin[S].Path := IncludeTrailingPathDelimiter(ExtractFileDir(FileName)); - Skin[S].FileName := ExtractFileName(FileName); + Skin[S].Path := FileName.GetPath; + Skin[S].FileName := FileName.GetName; Skin[S].Theme := SkinIni.ReadString('Skin', 'Theme', ''); Skin[S].Name := SkinIni.ReadString('Skin', 'Name', ''); Skin[S].Creator := SkinIni.ReadString('Skin', 'Creator', ''); @@ -130,15 +136,15 @@ end; procedure TSkin.LoadSkin(Name: string); var - SkinIni: TMemIniFile; - SL: TStringList; - T: integer; - S: integer; + SkinIni: TMemIniFile; + SL: TStringList; + T: integer; + S: integer; begin S := GetSkinNumber(Name); SkinPath := Skin[S].Path; - SkinIni := TMemIniFile.Create(SkinPath + Skin[S].FileName); + SkinIni := TMemIniFile.Create(SkinPath.Append(Skin[S].FileName).ToNative); SL := TStringList.Create; SkinIni.ReadSection('Textures', SL); @@ -147,48 +153,51 @@ begin for T := 0 to SL.Count-1 do begin SkinTexture[T].Name := SL.Strings[T]; - SkinTexture[T].FileName := SkinIni.ReadString('Textures', SL.Strings[T], ''); + SkinTexture[T].FileName := Path(SkinIni.ReadString('Textures', SL.Strings[T], '')); end; SL.Free; SkinIni.Free; end; -function TSkin.GetTextureFileName(TextureName: string): string; +function TSkin.GetTextureFileName(TextureName: string): IPath; var - T: integer; + T: integer; begin - Result := ''; + Result := PATH_NONE; for T := 0 to High(SkinTexture) do begin - if ( SkinTexture[T].Name = TextureName ) AND - ( SkinTexture[T].FileName <> '' ) then + if (SkinTexture[T].Name = TextureName) and + (SkinTexture[T].FileName.IsSet) then begin - Result := SkinPath + SkinTexture[T].FileName; + Result := SkinPath.Append(SkinTexture[T].FileName); end; end; - if ( TextureName <> '' ) AND - ( Result <> '' ) THEN + if (TextureName <> '') and (Result.IsSet) then begin //Log.LogError('', '-----------------------------------------'); //Log.LogError(TextureName+' - '+ Result, 'TSkin.GetTextureFileName'); end; { Result := SkinPath + 'Bar.jpg'; - if TextureName = 'Ball' then Result := SkinPath + 'Ball.bmp'; - if Copy(TextureName, 1, 4) = 'Gray' then Result := SkinPath + 'Ball.bmp'; - if Copy(TextureName, 1, 6) = 'NoteBG' then Result := SkinPath + 'Ball.bmp';} + if TextureName = 'Ball' then + Result := SkinPath + 'Ball.bmp'; + if Copy(TextureName, 1, 4) = 'Gray' then + Result := SkinPath + 'Ball.bmp'; + if Copy(TextureName, 1, 6) = 'NoteBG' then + Result := SkinPath + 'Ball.bmp';} end; function TSkin.GetSkinNumber(Name: string): integer; var - S: integer; + S: integer; begin Result := 0; // set default to the first available skin for S := 0 to High(Skin) do - if Skin[S].Name = Name then Result := S; + if Skin[S].Name = Name then + Result := S; end; procedure TSkin.onThemeChange; @@ -200,7 +209,8 @@ begin SetLength(ISkin, 0); Name := Uppercase(ITheme[Ini.Theme]); for S := 0 to High(Skin) do - if Name = Uppercase(Skin[S].Theme) then begin + if Name = Uppercase(Skin[S].Theme) then + begin SetLength(ISkin, Length(ISkin)+1); ISkin[High(ISkin)] := Skin[S].Name; end; diff --git a/Lua/src/base/USong.pas b/Lua/src/base/USong.pas index b1458e69..c465f198 100644 --- a/Lua/src/base/USong.pas +++ b/Lua/src/base/USong.pas @@ -56,7 +56,11 @@ uses PseudoThread, {$ENDIF} UCatCovers, - UXMLSong; + UXMLSong, + UUnicodeUtils, + UTextEncoding, + UFilesystem, + UPath; type @@ -68,42 +72,63 @@ type end; TScore = record - Name: WideString; + Name: UTF8String; Score: integer; - Length: string; + Date: UTF8String; + end; + + { used to hold header tags that are not supported by this version of + usdx (e.g. some tags from ultrastar 0.7.0) when songs are loaded in + songeditor. They will be written the end of the song header } + TCustomHeaderTag = record + Tag: UTF8String; + Content: UTF8String; end; TSong = class + private FileLineNo : integer; // line, which is read last, for error reporting - procedure ParseNote(LineNumber: integer; TypeP: char; StartP, DurationP, NoteP: integer; LyricS: string); + function DecodeFilename(Filename: RawByteString): IPath; + function Solmizate(Note: integer; Type_: integer): string; + procedure ParseNote(LineNumber: integer; TypeP: char; StartP, DurationP, NoteP: integer; LyricS: UTF8String); procedure NewSentence(LineNumberP: integer; Param1, Param2: integer); - function ReadTXTHeader( const aFileName : WideString ): boolean; - function ReadXMLHeader( const aFileName : WideString ): boolean; + function ParseLyricStringParam(const Line: RawByteString; var LinePos: integer): RawByteString; + function ParseLyricIntParam(const Line: RawByteString; var LinePos: integer): integer; + function ParseLyricFloatParam(const Line: RawByteString; var LinePos: integer): real; + function ParseLyricCharParam(const Line: RawByteString; var LinePos: integer): AnsiChar; + function ParseLyricText(const Line: RawByteString; var LinePos: integer): RawByteString; + + function ReadTXTHeader(SongFile: TTextFileStream; ReadCustomTags: Boolean): boolean; + function ReadXMLHeader(const aFileName: IPath): boolean; + + function GetFolderCategory(const aFileName: IPath): UTF8String; + function FindSongFile(Dir: IPath; Mask: UTF8String): IPath; public - Path: WideString; - Folder: WideString; // for sorting by folder - fFileName, - FileName: WideString; + Path: IPath; // kust path component of file (only set if file was found) + Folder: UTF8String; // for sorting by folder (only set if file was found) + FileName: IPath; // just name component of file (only set if file was found) + + // filenames + Cover: IPath; + Mp3: IPath; + Background: IPath; + Video: IPath; // sorting methods - Category: array of WideString; // TODO: do we need this? - Genre: WideString; - Edition: WideString; - Language: WideString; + Genre: UTF8String; + Edition: UTF8String; + Language: UTF8String; + Year: Integer; - Title: WideString; - Artist: WideString; + Title: UTF8String; + Artist: UTF8String; - Text: WideString; - Creator: WideString; + Creator: UTF8String; - Cover: WideString; CoverTex: TTexture; - Mp3: WideString; - Background: WideString; - Video: WideString; + VideoGAP: real; NotesGAP: integer; Start: real; // in seconds @@ -113,6 +138,10 @@ type BPM: array of TBPM; GAP: real; // in miliseconds + Encoding: TEncoding; + + CustomTags: array of TCustomHeaderTag; + Score: array[0..2] of array of TScore; // these are used when sorting is enabled @@ -122,23 +151,21 @@ type OrderTyp: integer; // type of sorting for this button (0=name) CatNumber: integer; // Count of Songs in Category for Cats and Number of Song in Category for Songs - SongFile: TextFile; // all procedures in this unit operate on this file - Base : array[0..1] of integer; Rel : array[0..1] of integer; Mult : integer; MultBPM : integer; - LastError: String; + LastError: AnsiString; function GetErrorLineNo: integer; property ErrorLineNo: integer read GetErrorLineNo; - constructor Create (); overload; - constructor Create ( const aFileName : WideString ); overload; + constructor Create(); overload; + constructor Create(const aFileName : IPath); overload; function LoadSong: boolean; function LoadXMLSong: boolean; - function Analyse(): boolean; + function Analyse(const ReadCustomTags: Boolean = false): boolean; function AnalyseXML(): boolean; procedure Clear(); end; @@ -146,33 +173,85 @@ type implementation uses + StrUtils, TextGL, UIni, + UPathUtils, UMusic, //needed for Lines - UMain; //needed for Player + UNote; //needed for Player + +const + DEFAULT_ENCODING = encAuto; constructor TSong.Create(); begin inherited; + + // to-do : special create for category "songs" + //dirty fix to fix folders=on + Self.Path := PATH_NONE(); + Self.FileName := PATH_NONE(); + Self.Cover := PATH_NONE(); + Self.Mp3 := PATH_NONE(); + Self.Background:= PATH_NONE(); + Self.Video := PATH_NONE(); end; -constructor TSong.Create( const aFileName : WideString ); +// This may be changed, when we rewrite song select code. +// it is some kind of dirty, but imho the best possible +// solution as we do atm not support nested categorys. +// it works like the folder sorting in 1.0.1a +// folder is set to the first folder under the songdir +// so songs ~/.ultrastardx/songs/punk is in the same +// category as songs in shared/ultrastardx/songs are. +// note: folder is just the name of a category it has +// nothing to do with the path used for file loading +function TSong.GetFolderCategory(const aFileName: IPath): UTF8String; +var + I: Integer; + CurSongPath: IPath; + CurSongPathRel: IPath; +begin + Result := 'Unknown'; //default folder category, if we can't locate the song dir + + for I := 0 to SongPaths.Count-1 do + begin + CurSongPath := SongPaths[I] as IPath; + if (aFileName.IsChildOf(CurSongPath, false)) then + begin + if (aFileName.IsChildOf(CurSongPath, true)) then + begin + // songs are in the "root" of the songdir => use songdir for the categorys name + Result := CurSongPath.RemovePathDelim.ToUTF8; + end + else + begin + // use the first subdirectory below CurSongPath as the category name + CurSongPathRel := aFileName.GetRelativePath(CurSongPath.AppendPathDelim); + Result := CurSongPathRel.SplitDirs[0].RemovePathDelim.ToUTF8; + end; + Exit; + end; + end; +end; + +constructor TSong.Create(const aFileName: IPath); begin inherited Create(); Mult := 1; MultBPM := 4; - fFileName := aFileName; LastError := ''; - if fileexists( aFileName ) then + Self.Path := aFileName.GetPath; + Self.FileName := aFileName.GetName; + Self.Folder := GetFolderCategory(aFileName); + + (* + if (aFileName.IsFile) then begin - self.Path := ExtractFilePath( aFileName ); - self.Folder := ExtractFilePath( aFileName ); - self.FileName := ExtractFileName( aFileName ); - (* - if ReadTXTHeader( aFileName ) then + if ReadTXTHeader(aFileName) then begin LoadSong(); end @@ -181,43 +260,178 @@ begin Log.LogError('Error Loading SongHeader, abort Song Loading'); Exit; end; - *) + end; + *) +end; + +function TSong.FindSongFile(Dir: IPath; Mask: UTF8String): IPath; +var + Iter: IFileIterator; + FileInfo: TFileInfo; + FileName: IPath; +begin + Iter := FileSystem.FileFind(Dir.Append(Mask), faDirectory); + if (Iter.HasNext) then + Result := Iter.Next.Name + else + Result := PATH_NONE; +end; + +function TSong.DecodeFilename(Filename: RawByteString): IPath; +begin + Result := UPath.Path(DecodeStringUTF8(Filename, Encoding)); +end; + +type + EUSDXParseException = class(Exception); + +{** + * Parses the Line string starting from LinePos for a parameter. + * Leading whitespace is trimmed, same applies to the first trailing whitespace. + * After the call LinePos will point to the position after the first trailing + * whitespace. + * + * Raises an EUSDXParseException if no string was found. + * + * Example: + * ParseLyricParam(Line:'Param0 Param1 Param2', LinePos:8, ...) + * -> Param:'Param1', LinePos:16 (= start of 'Param2') + *} +function TSong.ParseLyricStringParam(const Line: RawByteString; var LinePos: integer): RawByteString; +var + Start: integer; + OldLinePos: integer; +const + Whitespace = [#9, ' ']; +begin + OldLinePos := LinePos; + + Start := 0; + while (LinePos <= Length(Line)) do + begin + if (Line[LinePos] in Whitespace) then + begin + // check for end of param + if (Start > 0) then + Break; + end + // check for beginning of param + else if (Start = 0) then + begin + Start := LinePos; + end; + Inc(LinePos); + end; + + // check if param was found + if (Start = 0) then + begin + LinePos := OldLinePos; + raise EUSDXParseException.Create('String expected'); + end + else + begin + // copy param without trailing whitespace + Result := Copy(Line, Start, LinePos-Start); + // skip first trailing whitespace (if not at EOL) + if (LinePos <= Length(Line)) then + Inc(LinePos); + end; +end; + +function TSong.ParseLyricIntParam(const Line: RawByteString; var LinePos: integer): integer; +var + Str: RawByteString; + OldLinePos: integer; +begin + OldLinePos := LinePos; + Str := ParseLyricStringParam(Line, LinePos); + try + Result := StrToInt(Str); + except // on EConvertError + LinePos := OldLinePos; + raise EUSDXParseException.Create('Integer expected'); + end; +end; + +function TSong.ParseLyricFloatParam(const Line: RawByteString; var LinePos: integer): real; +var + Str: RawByteString; + OldLinePos: integer; +begin + OldLinePos := LinePos; + Str := ParseLyricStringParam(Line, LinePos); + try + Result := StrToFloat(Str); + except // on EConvertError + LinePos := OldLinePos; + raise EUSDXParseException.Create('Float expected'); + end; +end; + +function TSong.ParseLyricCharParam(const Line: RawByteString; var LinePos: integer): AnsiChar; +var + Str: RawByteString; + OldLinePos: integer; +begin + OldLinePos := LinePos; + Str := ParseLyricStringParam(Line, LinePos); + if (Length(Str) <> 1) then + begin + LinePos := OldLinePos; + raise EUSDXParseException.Create('Character expected'); + end; + Result := Str[1]; +end; + +{** + * Returns the rest of the line from LinePos as lyric text. + * Leading and trailing whitespace is not trimmed. + *} +function TSong.ParseLyricText(const Line: RawByteString; var LinePos: integer): RawByteString; +begin + if (LinePos > Length(Line)) then + Result := '' + else + begin + Result := Copy(Line, LinePos, Length(Line)-LinePos+1); + LinePos := Length(Line)+1; end; end; //Load TXT Song function TSong.LoadSong(): boolean; - var - TempC: char; - Text: string; - CP: integer; // Current Player (0 or 1) + CurLine: RawByteString; + LinePos: integer; Count: integer; Both: boolean; - Param1: integer; - Param2: integer; - Param3: integer; - ParamS: string; - I: integer; + Param0: AnsiChar; + Param1: integer; + Param2: integer; + Param3: integer; + ParamLyric: UTF8String; + + I: integer; + NotesFound: boolean; + SongFile: TTextFileStream; + FileNamePath: IPath; begin Result := false; LastError := ''; - if not FileExists(Path + PathDelim + FileName) then + FileNamePath := Path.Append(FileName); + if not FileNamePath.IsFile() then begin LastError := 'ERROR_CORRUPT_SONG_FILE_NOT_FOUND'; - Log.LogError('File not found: "' + Path + PathDelim + FileName + '"', 'TSong.LoadSong()'); - exit; + Log.LogError('File not found: "' + FileNamePath.ToNative + '"', '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].ScoreValue := 0; - self.Relative := false; Rel[0] := 0; - CP := 0; Both := false; if Length(Player) = 2 then @@ -225,187 +439,155 @@ begin try // Open song file for reading..... - FileMode := fmOpenRead; - AssignFile(SongFile, fFileName); - Reset(SongFile); - - //Clear old Song Header - if (self.Path = '') then - self.Path := ExtractFilePath(FileName); - - if (self.FileName = '') then - self.Filename := ExtractFileName(FileName); - - FileLineNo := 0; - //Search for Note Begining - repeat - ReadLn(SongFile, Text); - Inc(FileLineNo); + SongFile := TMemTextFileStream.Create(FileNamePath, fmOpenRead); + try + //Search for Note Beginning + FileLineNo := 0; + NotesFound := false; + while (SongFile.ReadLine(CurLine)) do + begin + Inc(FileLineNo); + if (Length(CurLine) > 0) and (CurLine[1] in [':', 'F', '*']) then + begin + NotesFound := true; + Break; + end; + end; - if (EoF(SongFile)) then + if (not NotesFound) then begin //Song File Corrupted - No Notes - CloseFile(SongFile); - Log.LogError('Could not load txt File, no Notes found: ' + FileName); + Log.LogError('Could not load txt File, no notes found: ' + FileNamePath.ToNative); LastError := 'ERROR_CORRUPT_SONG_NO_NOTES'; Exit; end; - Read(SongFile, TempC); - until ((TempC = ':') or (TempC = 'F') or (TempC = '*')); - - SetLength(Lines, 2); - 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].HighNote := -1; - Lines[Count].Line[0].LastLine := false; - end; - - //TempC := ':'; - //TempC := Text[1]; // read from backup variable, don't use default ':' value - - while (TempC <> 'E') and (not EOF(SongFile)) do - begin - - if (TempC = ':') or (TempC = '*') or (TempC = 'F') then - begin - // read notes - Read(SongFile, Param1); - Read(SongFile, Param2); - 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 - // add notes - if not Both then - // P1 - ParseNote(0, TempC, (Param1+Rel[0]) * Mult, Param2 * Mult, Param3, ParamS) - else - begin - // P1 + P2 - 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 // if - - else if TempC = '-' then - begin - // reads sentence - Read(SongFile, Param1); - if self.Relative then - Read(SongFile, Param2); // read one more data for relative system - - // new sentence - if not Both then - // P1 - NewSentence(0, (Param1 + Rel[0]) * Mult, Param2) - else - begin - // P1 + P2 - NewSentence(0, (Param1 + Rel[0]) * Mult, Param2); - NewSentence(1, (Param1 + Rel[1]) * Mult, Param2); - end; - end // if - else if TempC = 'B' then + SetLength(Lines, 2); + for Count := 0 to High(Lines) do begin - SetLength(self.BPM, Length(self.BPM) + 1); - Read(SongFile, self.BPM[High(self.BPM)].StartBeat); - self.BPM[High(self.BPM)].StartBeat := self.BPM[High(self.BPM)].StartBeat + Rel[0]; - - Read(SongFile, Text); - self.BPM[High(self.BPM)].BPM := StrToFloat(Text); - self.BPM[High(self.BPM)].BPM := self.BPM[High(self.BPM)].BPM * Mult * MultBPM; + Lines[Count].High := 0; + Lines[Count].Number := 1; + Lines[Count].Current := 0; + Lines[Count].Resolution := self.Resolution; + Lines[Count].NotesGAP := self.NotesGAP; + Lines[Count].ScoreValue := 0; + + //Add first line and set some standard values to fields + //see procedure NewSentence for further explantation + //concerning most of these values + SetLength(Lines[Count].Line, 1); + Lines[Count].Line[0].HighNote := -1; + Lines[Count].Line[0].LastLine := false; + Lines[Count].Line[0].BaseNote := High(Integer); + Lines[Count].Line[0].TotalNotes := 0; end; - - if not Both then + while true do begin - Lines[CP].Line[Lines[CP].High].BaseNote := Base[CP]; - Lines[CP].Line[Lines[CP].High].LyricWidth := glTextWidth(Lines[CP].Line[Lines[CP].High].Lyric); - //Total Notes Patch - Lines[CP].Line[Lines[CP].High].TotalNotes := 0; - for I := low(Lines[CP].Line[Lines[CP].High].Note) to high(Lines[CP].Line[Lines[CP].High].Note) do - begin - if (Lines[CP].Line[Lines[CP].High].Note[I].NoteType = ntGolden) then - Lines[CP].Line[Lines[CP].High].TotalNotes := Lines[CP].Line[Lines[CP].High].TotalNotes + Lines[CP].Line[Lines[CP].High].Note[I].Length; + LinePos := 0; - if (Lines[CP].Line[Lines[CP].High].Note[I].NoteType <> ntFreestyle) then - Lines[CP].Line[Lines[CP].High].TotalNotes := Lines[CP].Line[Lines[CP].High].TotalNotes + Lines[CP].Line[Lines[CP].High].Note[I].Length; - end; - //Total Notes Patch End - end - else - begin - for Count := 0 to High(Lines) do + Param0 := ParseLyricCharParam(CurLine, LinePos); + if (Param0 = 'E') then + begin + Break + end + else if (Param0 in [':', '*', 'F']) then begin - Lines[Count].Line[Lines[Count].High].BaseNote := Base[Count]; - Lines[Count].Line[Lines[Count].High].LyricWidth := glTextWidth(Lines[Count].Line[Lines[Count].High].Lyric); - //Total Notes Patch - Lines[Count].Line[Lines[Count].High].TotalNotes := 0; - for I := low(Lines[Count].Line[Lines[Count].High].Note) to high(Lines[Count].Line[Lines[Count].High].Note) do + // read notes + Param1 := ParseLyricIntParam(CurLine, LinePos); + Param2 := ParseLyricIntParam(CurLine, LinePos); + Param3 := ParseLyricIntParam(CurLine, LinePos); + ParamLyric := ParseLyricText(CurLine, LinePos); + + //Check for ZeroNote + if Param2 = 0 then + Log.LogError('Found zero-length note at "'+Param0+' '+IntToStr(Param1)+' '+IntToStr(Param2)+' '+IntToStr(Param3)+ParamLyric+'" -> Note ignored!') + else begin - if (Lines[Count].Line[Lines[Count].High].Note[I].NoteType = ntGolden) then - Lines[Count].Line[Lines[Count].High].TotalNotes := Lines[Count].Line[Lines[Count].High].TotalNotes + Lines[Count].Line[Lines[Count].High].Note[I].Length; - if (Lines[Count].Line[Lines[Count].High].Note[I].NoteType <> ntFreestyle) then - Lines[Count].Line[Lines[Count].High].TotalNotes := Lines[Count].Line[Lines[Count].High].TotalNotes + Lines[Count].Line[Lines[Count].High].Note[I].Length; + // add notes + if not Both then + // P1 + ParseNote(0, Param0, (Param1+Rel[0]) * Mult, Param2 * Mult, Param3, ParamLyric) + else + begin + // P1 + P2 + ParseNote(0, Param0, (Param1+Rel[0]) * Mult, Param2 * Mult, Param3, ParamLyric); + ParseNote(1, Param0, (Param1+Rel[1]) * Mult, Param2 * Mult, Param3, ParamLyric); + end; + end; //Zeronote check + end // if + + else if Param0 = '-' then + begin + // reads sentence + Param1 := ParseLyricIntParam(CurLine, LinePos); + if self.Relative then + Param2 := ParseLyricIntParam(CurLine, LinePos); // read one more data for relative system + + // new sentence + if not Both then + // P1 + NewSentence(0, (Param1 + Rel[0]) * Mult, Param2) + else + begin + // P1 + P2 + NewSentence(0, (Param1 + Rel[0]) * Mult, Param2); + NewSentence(1, (Param1 + Rel[1]) * Mult, Param2); end; - //Total Notes Patch End - end; - end; - ReadLn(SongFile); //Jump to next line in File, otherwise the next Read would catch the linebreak(e.g. #13 #10 on win32) - - Read(SongFile, TempC); - Inc(FileLineNo); - end; // while} - - CloseFile(SongFile); + end // if - for I := 0 to High(Lines) do - begin - if ((Both) or (I = 0)) then - begin - if (Length(Lines[I].Line) < 2) then + else if Param0 = 'B' then begin - LastError := 'ERROR_CORRUPT_SONG_NO_BREAKS'; - Log.LogError('Error Loading File, Can''t find any Linebreaks: "' + fFileName + '"'); - exit; - end; + SetLength(self.BPM, Length(self.BPM) + 1); + self.BPM[High(self.BPM)].StartBeat := ParseLyricFloatParam(CurLine, LinePos); + self.BPM[High(self.BPM)].StartBeat := self.BPM[High(self.BPM)].StartBeat + Rel[0]; - if (Lines[I].Line[Lines[I].High].HighNote < 0) then - begin - SetLength(Lines[I].Line, Lines[I].Number - 1); - Lines[I].High := Lines[I].High - 1; - Lines[I].Number := Lines[I].Number - 1; - Log.LogError('Error loading Song, sentence w/o note found in last line before E: ' + Filename); + self.BPM[High(self.BPM)].BPM := ParseLyricFloatParam(CurLine, LinePos); + self.BPM[High(self.BPM)].BPM := self.BPM[High(self.BPM)].BPM * Mult * MultBPM; end; - end; - end; - for Count := 0 to High(Lines) do - begin - if (High(Lines[Count].Line) >= 0) then - Lines[Count].Line[High(Lines[Count].Line)].LastLine := true; + // Read next line in File + if (not SongFile.ReadLine(CurLine)) then + Break; + + Inc(FileLineNo); + end; // while + finally + SongFile.Free; end; except - try - CloseFile(SongFile); - except + on E: Exception do + begin + Log.LogError(Format('Error loading file: "%s" in line %d,%d: %s', + [FileNamePath.ToNative, FileLineNo, LinePos, E.Message])); + Exit; + end; + end; + + for I := 0 to High(Lines) do + begin + if ((Both) or (I = 0)) then + begin + if (Length(Lines[I].Line) < 2) then + begin + LastError := 'ERROR_CORRUPT_SONG_NO_BREAKS'; + Log.LogError('Error loading file: Can''t find any linebreaks in "' + FileNamePath.ToNative + '"'); + exit; + end; + if (Lines[I].Line[Lines[I].High].HighNote < 0) then + begin + SetLength(Lines[I].Line, Lines[I].Number - 1); + Lines[I].High := Lines[I].High - 1; + Lines[I].Number := Lines[I].Number - 1; + Log.LogError('Error loading Song, sentence w/o note found in last line before E: ' + FileNamePath.ToNative); + end; end; + end; - LastError := 'ERROR_CORRUPT_SONG_ERROR_IN_LINE'; - Log.LogError('Error Loading File: "' + fFileName + '" in Line ' + inttostr(FileLineNo)); - exit; + for Count := 0 to High(Lines) do + begin + if (High(Lines[Count].Line) >= 0) then + Lines[Count].Line[High(Lines[Count].Line)].LastLine := true; end; Result := true; @@ -413,11 +595,7 @@ 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; @@ -430,24 +608,23 @@ var NoteType: char; SentenceEnd, Rest, Time: integer; Parser: TParser; - + FileNamePath: IPath; begin Result := false; LastError := ''; - if not FileExists(Path + PathDelim + FileName) then + FileNamePath := Path.Append(FileName); + if not FileNamePath.IsFile() then begin - Log.LogError('File not found: "' + Path + PathDelim + FileName + '"', 'TSong.LoadSong()'); + Log.LogError('File not found: "' + FileNamePath.ToNative + '"', '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].ScoreValue := 0; self.Relative := false; Rel[0] := 0; - CP := 0; Both := false; if Length(Player) = 2 then @@ -458,19 +635,26 @@ begin 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].HighNote := -1; - Lines[Count].Line[0].LastLine := false; + Lines[Count].Number := 1; + Lines[Count].Current := 0; + Lines[Count].Resolution := self.Resolution; + Lines[Count].NotesGAP := self.NotesGAP; + Lines[Count].ScoreValue := 0; + + //Add first line and set some standard values to fields + //see procedure NewSentence for further explantation + //concerning most of these values + SetLength(Lines[Count].Line, 1); + Lines[Count].Line[0].HighNote := -1; + Lines[Count].Line[0].LastLine := false; + Lines[Count].Line[0].BaseNote := High(Integer); + Lines[Count].Line[0].TotalNotes := 0; end; //Try to Parse the Song - if Parser.ParseSong(Path + PathDelim + FileName) then + if Parser.ParseSong(FileNamePath) then begin //Writeln('XML Inputfile Parsed succesful'); @@ -502,42 +686,6 @@ begin 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(Lines[CP].Line[Lines[CP].High].Lyric); - //Total Notes Patch - Lines[CP].Line[Lines[CP].High].TotalNotes := 0; - for NoteIndex := 0 to high(Lines[CP].Line[Lines[CP].High].Note) do - begin - if (Lines[CP].Line[Lines[CP].High].Note[NoteIndex].NoteType = ntGolden) then - Lines[CP].Line[Lines[CP].High].TotalNotes := Lines[CP].Line[Lines[CP].High].TotalNotes + Lines[CP].Line[Lines[CP].High].Note[NoteIndex].Length; - - if (Lines[CP].Line[Lines[CP].High].Note[NoteIndex].NoteType <> ntFreestyle) then - Lines[CP].Line[Lines[CP].High].TotalNotes := Lines[CP].Line[Lines[CP].High].TotalNotes + Lines[CP].Line[Lines[CP].High].Note[NoteIndex].Length; - 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(Lines[Count].Line[Lines[Count].High].Lyric); - //Total Notes Patch - Lines[Count].Line[Lines[Count].High].TotalNotes := 0; - for NoteIndex := 0 to high(Lines[Count].Line[Lines[Count].High].Note) do - begin - if (Lines[Count].Line[Lines[Count].High].Note[NoteIndex].NoteType = ntGolden) then - Lines[Count].Line[Lines[Count].High].TotalNotes := Lines[Count].Line[Lines[Count].High].TotalNotes + Lines[Count].Line[Lines[Count].High].Note[NoteIndex].Length; - if (Lines[Count].Line[Lines[Count].High].Note[NoteIndex].NoteType <> ntFreestyle) then - Lines[Count].Line[Lines[Count].High].TotalNotes := Lines[Count].Line[Lines[Count].High].TotalNotes + Lines[Count].Line[Lines[Count].High].Note[NoteIndex].Length; - - end; - //Total Notes Patch End - end; - end; { end of for loop } - end; //J Forloop //Add Sentence break @@ -573,7 +721,7 @@ begin end else begin - Log.LogError('Could not parse Inputfile: ' + Path + PathDelim + FileName); + Log.LogError('Could not parse inputfile: ' + FileNamePath.ToNative); exit; end; @@ -585,14 +733,11 @@ begin Result := true; end; -function TSong.ReadXMLHeader(const aFileName : WideString): boolean; - +function TSong.ReadXMLHeader(const aFileName : IPath): boolean; var - //Line, Identifier, Value: string; - //Temp : word; Done : byte; Parser : TParser; - + FileNamePath: IPath; begin Result := true; Done := 0; @@ -601,7 +746,8 @@ begin Parser := TParser.Create; Parser.Settings.DashReplacement := '~'; - if Parser.ParseSong(self.Path + self.FileName) then + FileNamePath := Self.Path.Append(Self.FileName); + if Parser.ParseSong(FileNamePath) then begin //----------- //Required Attributes @@ -620,9 +766,9 @@ begin Done := Done or 2; //MP3 File //Test if Exists - self.Mp3 := platform.FindSongFile(Path, '*.mp3'); + Self.Mp3 := FindSongFile(Self.Path, '*.mp3'); //Add Mp3 Flag to Done - if (FileExists(self.Path + self.Mp3)) then + if (Self.Path.Append(Self.Mp3).IsFile()) then Done := Done or 4; //Beats per Minute @@ -643,16 +789,16 @@ begin self.GAP := Parser.SongInfo.Header.Gap; //Cover Picture - self.Cover := platform.FindSongFile(Path, '*[CO].jpg'); + self.Cover := FindSongFile(Path, '*[CO].jpg'); //Background Picture - self.Background := platform.FindSongFile(Path, '*[BG].jpg'); + self.Background := FindSongFile(Path, '*[BG].jpg'); // Video File // self.Video := Value // Video Gap - // self.VideoGAP := song_StrtoFloat( Value ) + // self.VideoGAP := StrtoFloatI18n( Value ) //Genre Sorting self.Genre := Parser.SongInfo.Header.Genre; @@ -667,7 +813,7 @@ begin self.Language := Parser.SongInfo.Header.Language; end else - Log.LogError('File Incomplete or not SingStar XML (A): ' + aFileName); + Log.LogError('File incomplete or not SingStar XML (A): ' + aFileName.ToNative); Parser.Free; @@ -676,220 +822,297 @@ begin begin Result := false; if (Done and 8) = 0 then //No BPM Flag - Log.LogError('BPM Tag Missing: ' + self.FileName) + Log.LogError('BPM tag missing: ' + self.FileName.ToNative) else if (Done and 4) = 0 then //No MP3 Flag - Log.LogError('MP3 Tag/File Missing: ' + self.FileName) + Log.LogError('MP3 tag/file missing: ' + self.FileName.ToNative) else if (Done and 2) = 0 then //No Artist Flag - Log.LogError('Artist Tag Missing: ' + self.FileName) + Log.LogError('Artist tag missing: ' + self.FileName.ToNative) else if (Done and 1) = 0 then //No Title Flag - Log.LogError('Title Tag Missing: ' + self.FileName) + Log.LogError('Title tag missing: ' + self.FileName.ToNative) else //unknown Error - Log.LogError('File Incomplete or not SingStar XML (B - '+ inttostr(Done) +'): ' + aFileName); + Log.LogError('File incomplete or not SingStar XML (B - '+ inttostr(Done) +'): ' + aFileName.ToNative); end; end; +{** + * "International" StrToFloat variant. Uses either ',' or '.' as decimal + * separator. + *} +function StrToFloatI18n(const Value: string): extended; +var + TempValue : string; +begin + TempValue := Value; + if (Pos(',', TempValue) <> 0) then + TempValue[Pos(',', TempValue)] := '.'; + Result := StrToFloatDef(TempValue, 0); +end; -function TSong.ReadTXTHeader(const aFileName : WideString): boolean; - - function song_StrtoFloat( aValue : string ) : extended; - - var - lValue : string; - +function TSong.ReadTXTHeader(SongFile: TTextFileStream; ReadCustomTags: Boolean): boolean; +var + Line, Identifier: string; + Value: string; + SepPos: integer; // separator position + Done: byte; // bit-vector of mandatory fields + EncFile: IPath; // encoded filename + FullFileName: string; + + { adds a custom header tag to the song + if there is no ':' in the read line, Tag should be empty + and the whole line should be in Content } + procedure AddCustomTag(const Tag, Content: String); + var Len: Integer; begin - lValue := aValue; - - if (Pos(',', lValue) <> 0) then - lValue[Pos(',', lValue)] := '.'; - - Result := StrToFloatDef(lValue, 0); + if ReadCustomTags then + begin + Len := Length(CustomTags); + SetLength(CustomTags, Len + 1); + CustomTags[Len].Tag := DecodeStringUTF8(Tag, Encoding); + CustomTags[Len].Content := DecodeStringUTF8(Content, Encoding); + end; end; - -var - Line, Identifier, Value: string; - Temp : word; - Done : byte; - begin Result := true; Done := 0; - //Read first Line - ReadLn (SongFile, Line); + FullFileName := Path.Append(Filename).ToNative; + //Read first Line + SongFile.ReadLine(Line); if (Length(Line) <= 0) then begin - Log.LogError('File Starts with Empty Line: ' + aFileName); + Log.LogError('File starts with empty line: ' + FullFileName, + 'TSong.ReadTXTHeader'); Result := false; Exit; end; + // check if file begins with a UTF-8 BOM, if so set encoding to UTF-8 + if (CheckReplaceUTF8BOM(Line)) then + Encoding := encUTF8; + //Read Lines while Line starts with # or its empty while (Length(Line) = 0) or (Line[1] = '#') do begin //Increase Line Number Inc (FileLineNo); - Temp := Pos(':', Line); + SepPos := Pos(':', Line); - //Line has a Seperator-> Headerline - if (Temp <> 0) then + //Line has no Seperator, ignore non header field + if (SepPos = 0) then begin - //Read Identifier and Value - Identifier := Uppercase(Trim(Copy(Line, 2, Temp - 2))); //Uppercase is for Case Insensitive Checks - Value := Trim(Copy(Line, Temp + 1,Length(Line) - Temp)); - - //Check the Identifier (If Value is given) - if (Length(Value) <> 0) then + AddCustomTag('', Copy(Line, 2, Length(Line) - 1)); + // read next line + if (not SongFile.ReadLine(Line)) then begin - //----------- - //Required Attributes - //----------- + Result := false; + Log.LogError('File incomplete or not Ultrastar txt (A): ' + FullFileName); + Break; + end; + Continue; + end; - {$IFDEF UTF8_FILENAMES} - if ((Identifier = 'MP3') or (Identifier = 'BACKGROUND') or (Identifier = 'COVER') or (Identifier = 'VIDEO')) then - Value := Utf8Encode(Value); - {$ENDIF} + //Read Identifier and Value + Identifier := UpperCase(Trim(Copy(Line, 2, SepPos - 2))); //Uppercase is for Case Insensitive Checks + Value := Trim(Copy(Line, SepPos + 1, Length(Line) - SepPos)); - //Title - if (Identifier = 'TITLE') then - begin - self.Title := Value; + //Check the Identifier (If Value is given) + if (Length(Value) = 0) then + begin + Log.LogWarn('Empty field "'+Identifier+'" in file ' + FullFileName, + 'TSong.ReadTXTHeader'); + AddCustomTag(Identifier, ''); + end + else + begin - //Add Title Flag to Done - Done := Done or 1; - end + //----------- + //Required Attributes + //----------- - //Artist - else if (Identifier = 'ARTIST') then - begin - self.Artist := Value; + if (Identifier = 'TITLE') then + begin + DecodeStringUTF8(Value, Title, Encoding); + //Add Title Flag to Done + Done := Done or 1; + end - //Add Artist Flag to Done - Done := Done or 2; - end + else if (Identifier = 'ARTIST') then + begin + DecodeStringUTF8(Value, Artist, Encoding); + //Add Artist Flag to Done + Done := Done or 2; + end - //MP3 File //Test if Exists - else if (Identifier = 'MP3') and (FileExists(self.Path + Value)) then + //MP3 File + else if (Identifier = 'MP3') then + begin + EncFile := DecodeFilename(Value); + if (Self.Path.Append(EncFile).IsFile) then begin - self.Mp3 := Value; + self.Mp3 := EncFile; //Add Mp3 Flag to Done Done := Done or 4; - end + end; + end - //Beats per Minute - else if (Identifier = 'BPM') then + //Beats per Minute + else if (Identifier = 'BPM') then + begin + SetLength(self.BPM, 1); + self.BPM[0].StartBeat := 0; + + self.BPM[0].BPM := StrToFloatI18n( Value ) * Mult * MultBPM; + + if self.BPM[0].BPM <> 0 then begin - SetLength(self.BPM, 1); - self.BPM[0].StartBeat := 0; + //Add BPM Flag to Done + Done := Done or 8; + end; + end - self.BPM[0].BPM := song_StrtoFloat( Value ) * Mult * MultBPM; + //--------- + //Additional Header Information + //--------- - if self.BPM[0].BPM <> 0 then - begin - //Add BPM Flag to Done - Done := Done or 8; - end; - end + // Gap + else if (Identifier = 'GAP') then + begin + self.GAP := StrToFloatI18n(Value); + end - //--------- - //Additional Header Information - //--------- + //Cover Picture + else if (Identifier = 'COVER') then + begin + self.Cover := DecodeFilename(Value); + end - // Gap - else if (Identifier = 'GAP') then - self.GAP := song_StrtoFloat( Value ) + //Background Picture + else if (Identifier = 'BACKGROUND') then + begin + self.Background := DecodeFilename(Value); + end - //Cover Picture - else if (Identifier = 'COVER') then - self.Cover := Value + // Video File + else if (Identifier = 'VIDEO') then + begin + EncFile := DecodeFilename(Value); + if (self.Path.Append(EncFile).IsFile) then + self.Video := EncFile + else + Log.LogError('Can''t find video file in song: ' + FullFileName); + end - //Background Picture - else if (Identifier = 'BACKGROUND') then - self.Background := Value + // Video Gap + else if (Identifier = 'VIDEOGAP') then + begin + self.VideoGAP := StrToFloatI18n( Value ) + end - // Video File - else if (Identifier = 'VIDEO') then - begin - if (FileExists(self.Path + Value)) then - self.Video := Value - else - Log.LogError('Can''t find Video File in Song: ' + aFileName); - end + //Genre Sorting + else if (Identifier = 'GENRE') then + begin + DecodeStringUTF8(Value, Genre, Encoding) + end - // Video Gap - else if (Identifier = 'VIDEOGAP') then - self.VideoGAP := song_StrtoFloat( Value ) + //Edition Sorting + else if (Identifier = 'EDITION') then + begin + DecodeStringUTF8(Value, Edition, Encoding) + end - //Genre Sorting - else if (Identifier = 'GENRE') then - self.Genre := Value + //Creator Tag + else if (Identifier = 'CREATOR') then + begin + DecodeStringUTF8(Value, Creator, Encoding) + end - //Edition Sorting - else if (Identifier = 'EDITION') then - self.Edition := Value + //Language Sorting + else if (Identifier = 'LANGUAGE') then + begin + DecodeStringUTF8(Value, Language, Encoding) + end - //Creator Tag - else if (Identifier = 'CREATOR') then - self.Creator := Value + //Language Sorting + else if (Identifier = 'YEAR') then + begin + TryStrtoInt(Value, self.Year) + end - //Language Sorting - else if (Identifier = 'LANGUAGE') then - self.Language := Value + // Song Start + else if (Identifier = 'START') then + begin + self.Start := StrToFloatI18n( Value ) + end - // Song Start - else if (Identifier = 'START') then - self.Start := song_StrtoFloat( Value ) + // Song Ending + else if (Identifier = 'END') then + begin + TryStrtoInt(Value, self.Finish) + end - // Song Ending - else if (Identifier = 'END') then - TryStrtoInt(Value, self.Finish) + // Resolution + else if (Identifier = 'RESOLUTION') then + begin + TryStrtoInt(Value, self.Resolution) + end - // Resolution - else if (Identifier = 'RESOLUTION') then - TryStrtoInt(Value, self.Resolution) + // Notes Gap + else if (Identifier = 'NOTESGAP') then + begin + TryStrtoInt(Value, self.NotesGAP) + end - // Notes Gap - else if (Identifier = 'NOTESGAP') then - TryStrtoInt(Value, self.NotesGAP) - // Relative Notes - else if (Identifier = 'RELATIVE') and (uppercase(Value) = 'YES') then + // Relative Notes + else if (Identifier = 'RELATIVE') then + begin + if (UpperCase(Value) = 'YES') then self.Relative := true; + end + // File encoding + else if (Identifier = 'ENCODING') then + begin + self.Encoding := ParseEncoding(Value, DEFAULT_ENCODING); + end + + // unsupported tag + else + begin + AddCustomTag(Identifier, Value); end; - end; - if not EOF(SongFile) then - ReadLn (SongFile, Line) - else + end; // End check for non-empty Value + + // read next line + if (not SongFile.ReadLine(Line)) then begin Result := false; - Log.LogError('File Incomplete or not Ultrastar TxT (A): ' + aFileName); - break; + Log.LogError('File incomplete or not Ultrastar txt (A): ' + FullFileName); + Break; end; + end; // while - end; - - if self.Cover = '' then - self.Cover := platform.FindSongFile(Path, '*[CO].jpg'); + if self.Cover.IsUnset then + self.Cover := FindSongFile(Path, '*[CO].jpg'); //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) + Log.LogError('BPM tag missing: ' + FullFileName) else if (Done and 4) = 0 then //No MP3 Flag - Log.LogError('MP3 Tag/File Missing: ' + self.FileName) + Log.LogError('MP3 tag/file missing: ' + FullFileName) else if (Done and 2) = 0 then //No Artist Flag - Log.LogError('Artist Tag Missing: ' + self.FileName) + Log.LogError('Artist tag missing: ' + FullFileName) else if (Done and 1) = 0 then //No Title Flag - Log.LogError('Title Tag Missing: ' + self.FileName) + Log.LogError('Title tag missing: ' + FullFileName) else //unknown Error - Log.LogError('File Incomplete or not Ultrastar TxT (B - '+ inttostr(Done) +'): ' + aFileName); + Log.LogError('File incomplete or not Ultrastar txt (B - '+ inttostr(Done) +'): ' + FullFileName); end; - end; function TSong.GetErrorLineNo: integer; @@ -900,47 +1123,52 @@ begin Result := -1; end; -procedure TSong.ParseNote(LineNumber: integer; TypeP: char; StartP, DurationP, NoteP: integer; LyricS: string); - +function TSong.Solmizate(Note: integer; Type_: integer): string; begin - case Ini.Solmization of + case (Type_) of 1: // european begin - case (NoteP mod 12) of - 0..1: LyricS := ' do '; - 2..3: LyricS := ' re '; - 4: LyricS := ' mi '; - 5..6: LyricS := ' fa '; - 7..8: LyricS := ' sol '; - 9..10: LyricS := ' la '; - 11: LyricS := ' si '; + case (Note mod 12) of + 0..1: Result := ' do '; + 2..3: Result := ' re '; + 4: Result := ' mi '; + 5..6: Result := ' fa '; + 7..8: Result := ' sol '; + 9..10: Result := ' la '; + 11: Result := ' si '; end; end; 2: // japanese begin - case (NoteP mod 12) of - 0..1: LyricS := ' do '; - 2..3: LyricS := ' re '; - 4: LyricS := ' mi '; - 5..6: LyricS := ' fa '; - 7..8: LyricS := ' so '; - 9..10: LyricS := ' la '; - 11: LyricS := ' shi '; + case (Note mod 12) of + 0..1: Result := ' do '; + 2..3: Result := ' re '; + 4: Result := ' mi '; + 5..6: Result := ' fa '; + 7..8: Result := ' so '; + 9..10: Result := ' la '; + 11: Result := ' shi '; end; end; 3: // american begin - case (NoteP mod 12) of - 0..1: LyricS := ' do '; - 2..3: LyricS := ' re '; - 4: LyricS := ' mi '; - 5..6: LyricS := ' fa '; - 7..8: LyricS := ' sol '; - 9..10: LyricS := ' la '; - 11: LyricS := ' ti '; + case (Note mod 12) of + 0..1: Result := ' do '; + 2..3: Result := ' re '; + 4: Result := ' mi '; + 5..6: Result := ' fa '; + 7..8: Result := ' sol '; + 9..10: Result := ' la '; + 11: Result := ' ti '; end; end; end; // case +end; + +procedure TSong.ParseNote(LineNumber: integer; TypeP: char; StartP, DurationP, NoteP: integer; LyricS: UTF8String); +begin + if (Ini.Solmization <> 0) then + LyricS := Solmizate(NoteP, Ini.Solmization); with Lines[LineNumber].Line[Lines[LineNumber].High] do begin @@ -964,17 +1192,23 @@ begin '*': Note[HighNote].NoteType := ntGolden; end; - if (Note[HighNote].NoteType = ntGolden) then - Lines[LineNumber].ScoreValue := Lines[LineNumber].ScoreValue + Note[HighNote].Length; + //add this notes value ("notes length" * "notes scorefactor") to the current songs entire value + Inc(Lines[LineNumber].ScoreValue, Note[HighNote].Length * ScoreFactor[Note[HighNote].NoteType]); + + //and to the current lines entire value + Inc(TotalNotes, Note[HighNote].Length * ScoreFactor[Note[HighNote].NoteType]); - if (Note[HighNote].NoteType <> ntFreestyle) then - Lines[LineNumber].ScoreValue := Lines[LineNumber].ScoreValue + Note[HighNote].Length; Note[HighNote].Tone := NoteP; - if Note[HighNote].Tone < Base[LineNumber] then - Base[LineNumber] := Note[HighNote].Tone; - Note[HighNote].Text := Copy(LyricS, 2, 100); + //if a note w/ a deeper pitch then the current basenote is found + //we replace the basenote w/ the current notes pitch + if Note[HighNote].Tone < BaseNote then + BaseNote := Note[HighNote].Tone; + + Note[HighNote].Color := 1; // default color to 1 for editor + + DecodeStringUTF8(LyricS, Note[HighNote].Text, Encoding); Lyric := Lyric + Note[HighNote].Text; End_ := Note[HighNote].Start + Note[HighNote].Length; @@ -982,43 +1216,35 @@ begin end; procedure TSong.NewSentence(LineNumberP: integer; Param1, Param2: integer); - var I: integer; - begin if (Lines[LineNumberP].Line[Lines[LineNumberP].High].HighNote <> -1) then - begin //Update old Sentence if it has notes and create a new sentence - // Update old part - Lines[LineNumberP].Line[Lines[LineNumberP].High].BaseNote := Base[LineNumberP]; - Lines[LineNumberP].Line[Lines[LineNumberP].High].LyricWidth := glTextWidth(Lines[LineNumberP].Line[Lines[LineNumberP].High].Lyric); - - //Total Notes Patch - Lines[LineNumberP].Line[Lines[LineNumberP].High].TotalNotes := 0; - for I := low(Lines[LineNumberP].Line[Lines[LineNumberP].High].Note) to high(Lines[LineNumberP].Line[Lines[LineNumberP].High].Note) do - begin - if (Lines[LineNumberP].Line[Lines[LineNumberP].High].Note[I].NoteType = ntGolden) then - Lines[LineNumberP].Line[Lines[LineNumberP].High].TotalNotes := Lines[LineNumberP].Line[Lines[LineNumberP].High].TotalNotes + Lines[LineNumberP].Line[Lines[LineNumberP].High].Note[I].Length; - - if (Lines[LineNumberP].Line[Lines[LineNumberP].High].Note[I].NoteType <> ntFreestyle) then - Lines[LineNumberP].Line[Lines[LineNumberP].High].TotalNotes := Lines[LineNumberP].Line[Lines[LineNumberP].High].TotalNotes + Lines[LineNumberP].Line[Lines[LineNumberP].High].Note[I].Length; - end; - //Total Notes Patch End - - - // Update new part + begin //create a new line SetLength(Lines[LineNumberP].Line, Lines[LineNumberP].Number + 1); - Lines[LineNumberP].High := Lines[LineNumberP].High + 1; - Lines[LineNumberP].Number := Lines[LineNumberP].Number + 1; + Inc(Lines[LineNumberP].High); + Inc(Lines[LineNumberP].Number); end else - begin //Use old Sentence if it has no notes - Log.LogError('Error loading Song, sentence w/o note found in line ' + InttoStr(FileLineNo) + ': ' + Filename); + begin //use old line if it there were no notes added since last call of NewSentence + Log.LogError('Error loading Song, sentence w/o note found in line ' + + InttoStr(FileLineNo) + ': ' + Filename.ToNative); end; Lines[LineNumberP].Line[Lines[LineNumberP].High].HighNote := -1; + //set the current lines value to zero + //it will be incremented w/ the value of every added note + Lines[LineNumberP].Line[Lines[LineNumberP].High].TotalNotes := 0; + + //basenote is the pitch of the deepest note, it is used for note drawing. + //if a note with a less value than the current sentences basenote is found, + //basenote will be set to this notes pitch. Therefore the initial value of + //this field has to be very high. + Lines[LineNumberP].Line[Lines[LineNumberP].High].BaseNote := High(Integer); + + if self.Relative then begin Lines[LineNumberP].Line[Lines[LineNumberP].High].Start := Param1; @@ -1028,12 +1254,9 @@ begin Lines[LineNumberP].Line[Lines[LineNumberP].High].Start := Param1; Lines[LineNumberP].Line[Lines[LineNumberP].High].LastLine := false; - - Base[LineNumberP] := 100; // high number end; -procedure TSong.clear(); - +procedure TSong.Clear(); begin //Main Information Title := ''; @@ -1042,33 +1265,38 @@ begin //Sortings: Genre := 'Unknown'; Edition := 'Unknown'; - Language := 'Unknown'; //Language Patch + Language := 'Unknown'; + Year := 0; + + // set to default encoding + Encoding := DEFAULT_ENCODING; + + // clear custom header tags + SetLength(CustomTags, 0); //Required Information - Mp3 := ''; - {$IFDEF FPC} - setlength( BPM, 0 ); - {$ELSE} - BPM := nil; - {$ENDIF} + Mp3 := PATH_NONE; + SetLength(BPM, 0); GAP := 0; Start := 0; Finish := 0; //Additional Information - Background := ''; - Cover := ''; - Video := ''; + Background := PATH_NONE; + Cover := PATH_NONE; + Video := PATH_NONE; VideoGAP := 0; NotesGAP := 0; Resolution := 4; Creator := ''; + Relative := false; end; -function TSong.Analyse(): boolean; - +function TSong.Analyse(const ReadCustomTags: Boolean): boolean; +var + SongFile: TTextFileStream; begin Result := false; @@ -1076,20 +1304,15 @@ begin FileLineNo := 0; //Open File and set File Pointer to the beginning - AssignFile(SongFile, self.Path + self.FileName); - + SongFile := TMemTextFileStream.Create(Self.Path.Append(Self.FileName), fmOpenRead); try - Reset(SongFile); - //Clear old Song Header - self.clear; + Self.clear; //Read Header - Result := self.ReadTxTHeader( FileName ) - - //And Close File + Result := Self.ReadTxTHeader(SongFile, ReadCustomTags) finally - CloseFile(SongFile); + SongFile.Free; end; end; diff --git a/Lua/src/base/USongs.pas b/Lua/src/base/USongs.pas index 7f5125a9..baeec13a 100644 --- a/Lua/src/base/USongs.pas +++ b/Lua/src/base/USongs.pas @@ -40,74 +40,79 @@ interface {$ENDIF} uses + SysUtils, + Classes, {$IFDEF MSWINDOWS} Windows, DirWatch, {$ELSE} {$IFNDEF DARWIN} - syscall, + syscall, {$ENDIF} baseunix, UnixType, {$ENDIF} - SysUtils, - Classes, UPlatform, ULog, UTexture, UCommon, - {$IFDEF DARWIN} - cthreads, - {$ENDIF} {$IFDEF USE_PSEUDO_THREAD} - PseudoThread, + PseudoThread, {$ENDIF} + UPath, USong, UCatCovers; type + TSongFilter = ( + fltAll, + fltTitle, + fltArtist + ); TBPM = record - BPM: real; - StartBeat: real; + BPM: real; + StartBeat: real; end; TScore = record - Name: widestring; - Score: integer; - Length: string; + Name: UTF8String; + Score: integer; + Length: string; end; + TPathDynArray = array of IPath; + {$IFDEF USE_PSEUDO_THREAD} - TSongs = class( TPseudoThread ) + TSongs = class(TPseudoThread) {$ELSE} - TSongs = class( TThread ) + TSongs = class(TThread) {$ENDIF} private - fNotify, fWatch : longint; - fParseSongDirectory : boolean; - fProcessing : boolean; + fNotify, fWatch: longint; + fParseSongDirectory: boolean; + fProcessing: boolean; {$ifdef MSWINDOWS} - fDirWatch : TDirectoryWatch; + fDirWatch: TDirectoryWatch; {$endif} procedure int_LoadSongList; procedure DoDirChanged(Sender: TObject); protected procedure Execute; override; public - SongList : TList; // array of songs - Selected : integer; // selected song index + SongList: TList; // array of songs + Selected: integer; // selected song index constructor Create(); destructor Destroy(); override; 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 FindFilesByExtension(const Dir: IPath; const Ext: IPath; Recursive: Boolean; var Files: TPathDynArray); + procedure BrowseDir(Dir: IPath); // should return number of songs in the future + procedure BrowseTXTFiles(Dir: IPath); + procedure BrowseXMLFiles(Dir: IPath); procedure Sort(Order: integer); - function FindSongFile(Dir, Mask: widestring): widestring; - property Processing : boolean read fProcessing; + property Processing: boolean read fProcessing; end; @@ -116,24 +121,24 @@ type Selected: integer; // selected song index Order: integer; // order type (0=title) CatNumShow: integer; // Category Number being seen - CatCount: integer; //Number of Categorys + CatCount: integer; // Number of Categorys procedure SortSongs(); - procedure Refresh; // refreshes arrays by recreating them from Songs array - procedure ShowCategory(Index: integer); // expands all songs in category - procedure HideCategory(Index: integer); // hides all songs in category - procedure ClickCategoryButton(Index: integer); // uses ShowCategory and HideCategory when needed - procedure ShowCategoryList; //Hides all Songs And Show the List of all Categorys - function FindNextVisible(SearchFrom:integer): integer; //Find Next visible Song - function VisibleSongs: integer; // returns number of visible songs (for tabs) - function VisibleIndex(Index: integer): integer; // returns visible song index (skips invisible) - - function SetFilter(FilterStr: string; const fType: Byte): Cardinal; + procedure Refresh; // refreshes arrays by recreating them from Songs array + procedure ShowCategory(Index: integer); // expands all songs in category + procedure HideCategory(Index: integer); // hides all songs in category + procedure ClickCategoryButton(Index: integer); // uses ShowCategory and HideCategory when needed + procedure ShowCategoryList; // Hides all Songs And Show the List of all Categorys + function FindNextVisible(SearchFrom: integer): integer; // Find Next visible Song + function VisibleSongs: integer; // returns number of visible songs (for tabs) + function VisibleIndex(Index: integer): integer; // returns visible song index (skips invisible) + + function SetFilter(FilterStr: UTF8String; Filter: TSongFilter): cardinal; end; var - Songs: TSongs; // all songs - CatSongs: TCatSongs; // categorized songs + Songs: TSongs; // all songs + CatSongs: TCatSongs; // categorized songs const IN_ACCESS = $00000001; //* File was accessed */ @@ -151,12 +156,17 @@ const implementation -uses StrUtils, - UGraphic, - UCovers, - UFiles, - UMain, - UIni; +uses + StrUtils, + UCovers, + UFiles, + UGraphic, + UMain, + UIni, + UPathUtils, + UNote, + UFilesystem, + UUnicodeUtils; constructor TSongs.Create(); begin @@ -168,7 +178,7 @@ begin // FIXME: threaded loading does not work this way. // It will just cause crashes but nothing else at the moment. - (* +(* {$ifdef MSWINDOWS} fDirWatch := TDirectoryWatch.create(nil); fDirWatch.OnChange := DoDirChanged; @@ -179,7 +189,7 @@ begin // now we can start the thread Resume(); - *) +*) // until it is fixed, simply load the song-list int_LoadSongList(); @@ -187,7 +197,7 @@ end; destructor TSongs.Destroy(); begin - FreeAndNil( SongList ); + FreeAndNil(SongList); inherited; end; @@ -198,7 +208,7 @@ end; procedure TSongs.Execute(); var - fChangeNotify : THandle; + fChangeNotify: THandle; begin {$IFDEF USE_PSEUDO_THREAD} int_LoadSongList(); @@ -230,15 +240,15 @@ begin // browse directories for I := 0 to SongPaths.Count-1 do - BrowseDir(SongPaths[I]); + BrowseDir(SongPaths[I] as IPath); - if assigned( CatSongs ) then + if assigned(CatSongs) then CatSongs.Refresh; - if assigned( CatCovers ) then + if assigned(CatCovers) then CatCovers.Load; - //if assigned( Covers ) then + //if assigned(Covers) then // Covers.Load; if assigned(ScreenSong) then @@ -262,84 +272,92 @@ begin Resume(); end; -procedure TSongs.BrowseDir(Dir: widestring); +procedure TSongs.BrowseDir(Dir: IPath); begin - BrowseTXTFiles(Dir); - BrowseXMLFiles(Dir); + BrowseTXTFiles(Dir); + BrowseXMLFiles(Dir); end; -procedure TSongs.BrowseTXTFiles(Dir: widestring); +procedure TSongs.FindFilesByExtension(const Dir: IPath; const Ext: IPath; Recursive: Boolean; var Files: TPathDynArray); var - i : integer; - Files : TDirectoryEntryArray; - lSong : TSong; + Iter: IFileIterator; + FileInfo: TFileInfo; + FileName: IPath; begin - - try - Files := Platform.DirectoryFindFiles( Dir, '.txt', true) - except - Log.LogError('Couldn''t deal with directory/file: ' + Dir + ' in TSongs.BrowseTXTFiles') - end; - - for i := 0 to Length(Files)-1 do + // search for all files and directories + Iter := FileSystem.FileFind(Dir.Append('*'), faAnyFile); + while (Iter.HasNext) do begin - if Files[i].IsDirectory then + FileInfo := Iter.Next; + FileName := FileInfo.Name; + if ((FileInfo.Attr and faDirectory) <> 0) then begin - BrowseTXTFiles( Dir + Files[i].Name + PathDelim ); //Recursive Call + if Recursive and (not FileName.Equals('.')) and (not FileName.Equals('..')) then + FindFilesByExtension(Dir.Append(FileName), Ext, true, Files); end else begin - lSong := TSong.create( Dir + Files[i].Name ); - - if lSong.Analyse then - SongList.add( lSong ) - else + if (Ext.Equals(FileName.GetExtension(), true)) then begin - Log.LogError('AnalyseFile failed for "' + Files[i].Name + '".'); - freeandnil( lSong ); + SetLength(Files, Length(Files)+1); + Files[High(Files)] := Dir.Append(FileName); end; - end; end; - SetLength( Files, 0); - end; -procedure TSongs.BrowseXMLFiles(Dir: widestring); +procedure TSongs.BrowseTXTFiles(Dir: IPath); var - i : integer; - Files : TDirectoryEntryArray; - lSong : TSong; + I: integer; + Files: TPathDynArray; + Song: TSong; + Extension: IPath; begin + SetLength(Files, 0); + Extension := Path('.txt'); + FindFilesByExtension(Dir, Extension, true, Files); - try - Files := Platform.DirectoryFindFiles( Dir, '.xml', true) - except - Log.LogError('Couldn''t deal with directory/file: ' + Dir + ' in TSongs.BrowseXMLFiles') - end; - - for i := 0 to Length(Files)-1 do + for I := 0 to High(Files) do begin - if Files[i].IsDirectory then - begin - BrowseXMLFiles( Dir + Files[i].Name + PathDelim ); //Recursive Call - end + Song := TSong.Create(Files[I]); + + if Song.Analyse then + SongList.Add(Song) else begin - lSong := TSong.create( Dir + Files[i].Name ); + Log.LogError('AnalyseFile failed for "' + Files[I].ToNative + '".'); + FreeAndNil(Song); + end; + end; - if lSong.AnalyseXML then - SongList.add( lSong ) - else - begin - Log.LogError('AnalyseFile failed for "' + Files[i].Name + '".'); - freeandnil( lSong ); - end; + SetLength(Files, 0); +end; +procedure TSongs.BrowseXMLFiles(Dir: IPath); +var + I: integer; + Files: TPathDynArray; + Song: TSong; + Extension: IPath; +begin + SetLength(Files, 0); + Extension := Path('.xml'); + FindFilesByExtension(Dir, Extension, true, Files); + + for I := 0 to High(Files) do + begin + Song := TSong.Create(Files[I]); + + if Song.AnalyseXML then + SongList.Add(Song) + else + begin + Log.LogError('AnalyseFile failed for "' + Files[I].ToNative + '".'); + FreeAndNil(Song); end; end; - SetLength( Files, 0); + SetLength(Files, 0); end; (* @@ -348,32 +366,32 @@ end; function CompareByEdition(Song1, Song2: Pointer): integer; begin - Result := CompareText(TSong(Song1).Edition, TSong(Song2).Edition); + Result := UTF8CompareText(TSong(Song1).Edition, TSong(Song2).Edition); end; function CompareByGenre(Song1, Song2: Pointer): integer; begin - Result := CompareText(TSong(Song1).Genre, TSong(Song2).Genre); + Result := UTF8CompareText(TSong(Song1).Genre, TSong(Song2).Genre); end; function CompareByTitle(Song1, Song2: Pointer): integer; begin - Result := CompareText(TSong(Song1).Title, TSong(Song2).Title); + Result := UTF8CompareText(TSong(Song1).Title, TSong(Song2).Title); end; function CompareByArtist(Song1, Song2: Pointer): integer; begin - Result := CompareText(TSong(Song1).Artist, TSong(Song2).Artist); + Result := UTF8CompareText(TSong(Song1).Artist, TSong(Song2).Artist); end; function CompareByFolder(Song1, Song2: Pointer): integer; begin - Result := CompareText(TSong(Song1).Folder, TSong(Song2).Folder); + Result := UTF8CompareText(TSong(Song1).Folder, TSong(Song2).Folder); end; function CompareByLanguage(Song1, Song2: Pointer): integer; begin - Result := CompareText(TSong(Song1).Language, TSong(Song2).Language); + Result := UTF8CompareText(TSong(Song1).Language, TSong(Song2).Language); end; procedure TSongs.Sort(Order: integer); @@ -392,8 +410,6 @@ begin CompareFunc := CompareByArtist; sFolder: // by folder CompareFunc := CompareByFolder; - sTitle2: // by title2 - CompareFunc := CompareByTitle; sArtist2: // by artist2 CompareFunc := CompareByArtist; sLanguage: // by Language @@ -410,18 +426,6 @@ begin MergeSort(SongList, CompareFunc); end; -function TSongs.FindSongFile(Dir, Mask: widestring): widestring; -var - SR: TSearchRec; // for parsing song directory -begin - Result := ''; - if FindFirst(Dir + Mask, faDirectory, SR) = 0 then - begin - Result := SR.Name; - end; // if - FindClose(SR); -end; - procedure TCatSongs.SortSongs(); begin case Ini.Sorting of @@ -452,12 +456,8 @@ begin Songs.Sort(sTitle); Songs.Sort(sArtist); end; - sTitle2: begin - Songs.Sort(sArtist2); - Songs.Sort(sTitle2); - end; sArtist2: begin - Songs.Sort(sTitle2); + Songs.Sort(sTitle); Songs.Sort(sArtist2); end; end; // case @@ -467,27 +467,27 @@ procedure TCatSongs.Refresh; var SongIndex: integer; CurSong: TSong; - CatIndex: integer; // index of current song in Song - Letter: char; // current letter for sorting using letter - CurCategory: string; // current edition for sorting using edition, genre etc. - Order: integer; // number used for ordernum - LetterTmp: char; - CatNumber: integer; // Number of Song in Category - - procedure AddCategoryButton(const CategoryName: string); + CatIndex: integer; // index of current song in Song + Letter: UCS4Char; // current letter for sorting using letter + CurCategory: UTF8String; // current edition for sorting using edition, genre etc. + Order: integer; // number used for ordernum + LetterTmp: UCS4Char; + CatNumber: integer; // Number of Song in Category + + procedure AddCategoryButton(const CategoryName: UTF8String); var PrevCatBtnIndex: integer; begin Inc(Order); CatIndex := Length(Song); SetLength(Song, CatIndex+1); - Song[CatIndex] := TSong.Create(); - Song[CatIndex].Artist := '[' + CategoryName + ']'; - Song[CatIndex].Main := true; + Song[CatIndex] := TSong.Create(); + Song[CatIndex].Artist := '[' + CategoryName + ']'; + Song[CatIndex].Main := true; Song[CatIndex].OrderTyp := 0; Song[CatIndex].OrderNum := Order; - Song[CatIndex].Cover := CatCovers.GetCover(Ini.Sorting, CategoryName); - Song[CatIndex].Visible := true; + Song[CatIndex].Cover := CatCovers.GetCover(Ini.Sorting, CategoryName); + Song[CatIndex].Visible := true; // set number of songs in previous category PrevCatBtnIndex := CatIndex - CatNumber - 1; @@ -498,21 +498,21 @@ var end; begin - CatNumShow := -1; + CatNumShow := -1; SortSongs(); CurCategory := ''; - Order := 0; - CatNumber := 0; + Order := 0; + CatNumber := 0; // Note: do NOT set Letter to ' ', otherwise no category-button will be // created for songs beginning with ' ' if songs of this category exist. // TODO: trim song-properties so ' ' will not occur as first chararcter. - Letter := #0; + Letter := 0; // clear song-list - for SongIndex := 0 to Songs.SongList.Count-1 do + for SongIndex := 0 to Songs.SongList.Count - 1 do begin // free category buttons // Note: do NOT delete songs, they are just references to Songs.SongList entries @@ -522,107 +522,108 @@ begin end; SetLength(Song, 0); - for SongIndex := 0 to Songs.SongList.Count-1 do + for SongIndex := 0 to Songs.SongList.Count - 1 do begin CurSong := TSong(Songs.SongList[SongIndex]); // if tabs are on, add section buttons for each new section if (Ini.Tabs = 1) then begin - if (Ini.Sorting = sEdition) and - (CompareText(CurCategory, CurSong.Edition) <> 0) then - begin - CurCategory := CurSong.Edition; - - // TODO: remove this block if it is not needed anymore - { - if CurSection = 'Singstar Part 2' then CoverName := 'Singstar'; - if CurSection = 'Singstar German' then CoverName := 'Singstar'; - if CurSection = 'Singstar Spanish' then CoverName := 'Singstar'; - if CurSection = 'Singstar Italian' then CoverName := 'Singstar'; - if CurSection = 'Singstar French' then CoverName := 'Singstar'; - if CurSection = 'Singstar 80s Polish' then CoverName := 'Singstar 80s'; - } - - // add Category Button - AddCategoryButton(CurCategory); - end - - else if (Ini.Sorting = sGenre) and - (CompareText(CurCategory, CurSong.Genre) <> 0) then - begin - CurCategory := CurSong.Genre; - // add Genre Button - AddCategoryButton(CurCategory); - end - - else if (Ini.Sorting = sLanguage) and - (CompareText(CurCategory, CurSong.Language) <> 0) then - begin - CurCategory := CurSong.Language; - // add Language Button - AddCategoryButton(CurCategory); - end - - else if (Ini.Sorting = sTitle) and - (Length(CurSong.Title) >= 1) and - (Letter <> UpperCase(CurSong.Title)[1]) then - begin - Letter := Uppercase(CurSong.Title)[1]; - // add a letter Category Button - AddCategoryButton(Letter); - end + case (Ini.Sorting) of + sEdition: begin + if (CompareText(CurCategory, CurSong.Edition) <> 0) then + begin + CurCategory := CurSong.Edition; + + // add Category Button + AddCategoryButton(CurCategory); + end; + end; - else if (Ini.Sorting = sArtist) and - (Length(CurSong.Artist) >= 1) and - (Letter <> UpperCase(CurSong.Artist)[1]) then - begin - Letter := UpperCase(CurSong.Artist)[1]; - // add a letter Category Button - AddCategoryButton(Letter); - end + sGenre: begin + if (CompareText(CurCategory, CurSong.Genre) <> 0) then + begin + CurCategory := CurSong.Genre; + // add Genre Button + AddCategoryButton(CurCategory); + end; + end; - else if (Ini.Sorting = sFolder) and - (CompareText(CurCategory, CurSong.Folder) <> 0) then - begin - CurCategory := CurSong.Folder; - // add folder tab - AddCategoryButton(CurCategory); - end + sLanguage: begin + if (CompareText(CurCategory, CurSong.Language) <> 0) then + begin + CurCategory := CurSong.Language; + // add Language Button + AddCategoryButton(CurCategory); + end + end; - else if (Ini.Sorting = sTitle2) and - (Length(CurSong.Title) >= 1) then - begin - // pack all numbers into a category named '#' - if (CurSong.Title[1] >= '0') and (CurSong.Title[1] <= '9') then - LetterTmp := '#' - else - LetterTmp := UpperCase(CurSong.Title)[1]; + sTitle: begin + if (Length(CurSong.Title) >= 1) then + begin + LetterTmp := UCS4UpperCase(UTF8ToUCS4String(CurSong.Title)[0]); + { all numbers and some punctuation chars are put into a + category named '#' + we can't put the other punctuation chars into this category + because they are not in order, so there will be two different + categories named '#' } + if (LetterTmp in [Ord('!') .. Ord('?')]) then + LetterTmp := Ord('#') + else + LetterTmp := UCS4UpperCase(LetterTmp); + if (Letter <> LetterTmp) then + begin + Letter := LetterTmp; + // add a letter Category Button + AddCategoryButton(UCS4ToUTF8String(Letter)); + end; + end; + end; - if (Letter <> LetterTmp) then - begin - Letter := LetterTmp; - // add a letter Category Button - AddCategoryButton(Letter); + sArtist: begin + if (Length(CurSong.Artist) >= 1) then + begin + LetterTmp := UCS4UpperCase(UTF8ToUCS4String(CurSong.Artist)[0]); + { all numbers and some punctuation chars are put into a + category named '#' + we can't put the other punctuation chars into this category + because they are not in order, so there will be two different + categories named '#' } + if (LetterTmp in [Ord('!') .. Ord('?')]) then + LetterTmp := Ord('#') + else + LetterTmp := UCS4UpperCase(LetterTmp); + + if (Letter <> LetterTmp) then + begin + Letter := LetterTmp; + // add a letter Category Button + AddCategoryButton(UCS4ToUTF8String(Letter)); + end; + end; end; - end - else if (Ini.Sorting = sArtist2) and - (Length(CurSong.Artist)>=1) then - begin - // pack all numbers into a category named '#' - if (CurSong.Artist[1] >= '0') and (CurSong.Artist[1] <= '9') then - LetterTmp := '#' - else - LetterTmp := UpperCase(CurSong.Artist)[1]; + sFolder: begin + if (UTF8CompareText(CurCategory, CurSong.Folder) <> 0) then + begin + CurCategory := CurSong.Folder; + // add folder tab + AddCategoryButton(CurCategory); + end; + end; - if (Letter <> LetterTmp) then - begin - Letter := LetterTmp; - // add a letter Category Button - AddCategoryButton(Letter); + sArtist2: begin + { this new sorting puts all songs by the same artist into + a single category } + if (UTF8CompareText(CurCategory, CurSong.Artist) <> 0) then + begin + CurCategory := CurSong.Artist; + // add folder tab + AddCategoryButton(CurCategory); + end; end; - end; - end; + + end; // case (Ini.Sorting) + end; // if (Ini.Tabs = 1) CatIndex := Length(Song); SetLength(Song, CatIndex+1); @@ -640,19 +641,18 @@ begin CurSong.Visible := true else if (Ini.Tabs = 1) then CurSong.Visible := false; - - { +{ if (Ini.Tabs = 1) and (Order = 1) then begin //open first tab CurSong.Visible := true; end; CurSong.Visible := true; - } +} end; // set CatNumber of last category - if (Ini.Tabs_at_startup = 1) and (High(Song) >= 1) then + if (Ini.TabsAtStartup = 1) and (High(Song) >= 1) then begin // set number of songs in previous category SongIndex := CatIndex - CatNumber; @@ -666,7 +666,7 @@ end; procedure TCatSongs.ShowCategory(Index: integer); var - S: integer; // song + S: integer; // song begin CatNumShow := Index; for S := 0 to high(CatSongs.Song) do @@ -678,13 +678,13 @@ begin CatSongs.Song[S].Visible := false; } // KMS: This should be the same, but who knows :-) - CatSongs.Song[S].Visible := ( (CatSongs.Song[S].OrderNum = Index) and (not CatSongs.Song[S].Main) ); + CatSongs.Song[S].Visible := ((CatSongs.Song[S].OrderNum = Index) and (not CatSongs.Song[S].Main)); end; end; procedure TCatSongs.HideCategory(Index: integer); // hides all songs in category var - S: integer; // song + S: integer; // song begin for S := 0 to high(CatSongs.Song) do begin @@ -695,7 +695,7 @@ end; procedure TCatSongs.ClickCategoryButton(Index: integer); var - Num: integer; + Num: integer; begin Num := CatSongs.Song[Index].OrderNum; if Num <> CatNumShow then @@ -711,7 +711,7 @@ end; //Hide Categorys when in Category Hack procedure TCatSongs.ShowCategoryList; var - S: integer; + S: integer; begin // Hide All Songs Show All Cats for S := 0 to high(CatSongs.Song) do @@ -721,23 +721,27 @@ begin end; //Hide Categorys when in Category Hack End -//Wrong song selected when tabs on bug -function TCatSongs.FindNextVisible(SearchFrom:integer): integer;//Find next Visible Song +// Wrong song selected when tabs on bug +function TCatSongs.FindNextVisible(SearchFrom:integer): integer;// Find next Visible Song var I: integer; begin Result := -1; - I := SearchFrom + 1; - while not CatSongs.Song[I].Visible do + I := SearchFrom; + while (Result = -1) do begin Inc (I); - if (I>high(CatSongs.Song)) then - I := low(CatSongs.Song); - if (I = SearchFrom) then //Make One Round and no song found->quit - break; + + if (I > High(CatSongs.Song)) then + I := Low(CatSongs.Song); + if (I = SearchFrom) then // Make One Round and no song found->quit + Break; + + if (CatSongs.Song[I].Visible) then + Result := I; end; end; -//Wrong song selected when tabs on bug End +// Wrong song selected when tabs on bug End (** * Returns the number of visible songs. @@ -763,71 +767,73 @@ var SongIndex: integer; begin Result := 0; - for SongIndex := 0 to Index-1 do + for SongIndex := 0 to Index - 1 do begin if (CatSongs.Song[SongIndex].Visible) then Inc(Result); end; end; -function TCatSongs.SetFilter(FilterStr: string; const fType: Byte): Cardinal; +function TCatSongs.SetFilter(FilterStr: UTF8String; Filter: TSongFilter): cardinal; var - I, J: integer; - cString: string; - SearchStr: array of string; + I, J: integer; + TmpString: UTF8String; + WordArray: array of UTF8String; begin - {fType: 0: All - 1: Title - 2: Artist} FilterStr := Trim(FilterStr); - if FilterStr<>'' then + if (FilterStr <> '') then begin Result := 0; - //Create Search Array - SetLength(SearchStr, 1); - I := Pos (' ', FilterStr); + + // initialize word array + SetLength(WordArray, 1); + + // Copy words to SearchStr + I := Pos(' ', FilterStr); while (I <> 0) do begin - SetLength (SearchStr, Length(SearchStr) + 1); - cString := Copy(FilterStr, 1, I-1); - if (cString <> ' ') and (cString <> '') then - SearchStr[High(SearchStr)-1] := cString; - Delete (FilterStr, 1, I); + WordArray[High(WordArray)] := Copy(FilterStr, 1, I-1); + SetLength(WordArray, Length(WordArray) + 1); - I := Pos (' ', FilterStr); + FilterStr := TrimLeft(Copy(FilterStr, I+1, Length(FilterStr)-I)); + I := Pos(' ', FilterStr); end; - //Copy last Word - if (FilterStr <> ' ') and (FilterStr <> '') then - SearchStr[High(SearchStr)] := FilterStr; - for I:=0 to High(Song) do + // Copy last word + WordArray[High(WordArray)] := FilterStr; + + for I := 0 to High(Song) do begin if not Song[i].Main then begin - case fType of - 0: cString := Song[I].Artist + ' ' + Song[i].Title + ' ' + Song[i].Folder; - 1: cString := Song[I].Title; - 2: cString := Song[I].Artist; + case Filter of + fltAll: + TmpString := Song[I].Artist + ' ' + Song[i].Title + ' ' + Song[i].Folder; + fltTitle: + TmpString := Song[I].Title; + fltArtist: + TmpString := Song[I].Artist; end; - Song[i].Visible:=True; - //Look for every Searched Word - for J := 0 to High(SearchStr) do + Song[i].Visible := true; + // Look for every searched word + for J := 0 to High(WordArray) do begin - Song[i].Visible := Song[i].Visible and AnsiContainsText(cString, SearchStr[J]) + Song[i].Visible := Song[i].Visible and + UTF8ContainsText(TmpString, WordArray[J]) end; if Song[i].Visible then Inc(Result); end else - Song[i].Visible:=False; + Song[i].Visible := false; end; CatNumShow := -2; end else begin - for i:=0 to High(Song) do + for i := 0 to High(Song) do begin - Song[i].Visible := (Ini.Tabs=1) = Song[i].Main; + Song[i].Visible := (Ini.Tabs = 1) = Song[i].Main; CatNumShow := -1; end; Result := 0; diff --git a/Lua/src/base/UTextEncoding.pas b/Lua/src/base/UTextEncoding.pas index 6eec8eec..148cd5d4 100644 --- a/Lua/src/base/UTextEncoding.pas +++ b/Lua/src/base/UTextEncoding.pas @@ -19,8 +19,8 @@ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. * - * $URL: https://ultrastardx.svn.sourceforge.net/svnroot/ultrastardx/trunk/src/menu/UMenuText.pas $ - * $Id: UMenuText.pas 1485 2008-10-28 20:16:05Z tobigun $ + * $URL$ + * $Id$ *} unit UTextEncoding; @@ -34,114 +34,214 @@ interface {$I switches.inc} uses - SysUtils; + SysUtils, + UUnicodeUtils; type - TEncoding = (encCP1250, encCP1252, encUTF8, encNative); + TEncoding = ( + encLocale, // current locale (needs cwstring on linux) + encUTF8, // UTF-8 + encCP1250, // Windows-1250 Central/Eastern Europe (used by Ultrastar) + encCP1252, // Windows-1252 Western Europe (used by UltraStar Deluxe < 1.1) + encAuto // try to match the w3c regex and decode as unicode on match + // and as fallback if not match + ); + +const + UTF8_BOM: UTF8String = #$EF#$BB#$BF; + +{** + * Decodes Src encoded in SrcEncoding to a UTF-16 or UTF-8 encoded Dst string. + * Returns true if the conversion was successful. + *} +function DecodeString(const Src: RawByteString; out Dst: WideString; SrcEncoding: TEncoding): boolean; overload; +function DecodeString(const Src: RawByteString; SrcEncoding: TEncoding): WideString; overload; +function DecodeStringUTF8(const Src: RawByteString; out Dst: UTF8String; SrcEncoding: TEncoding): boolean; overload; +function DecodeStringUTF8(const Src: RawByteString; SrcEncoding: TEncoding): UTF8String; overload; + +{** + * Encodes the UTF-16 or UTF-8 encoded Src string to Dst using DstEncoding + * Returns true if the conversion was successful. + *} +function EncodeString(const Src: WideString; out Dst: RawByteString; DstEncoding: TEncoding): boolean; overload; +function EncodeString(const Src: WideString; DstEncoding: TEncoding): RawByteString; overload; +function EncodeStringUTF8(const Src: UTF8String; out Dst: RawByteString; DstEncoding: TEncoding): boolean; overload; +function EncodeStringUTF8(const Src: UTF8String; DstEncoding: TEncoding): RawByteString; overload; + +{** + * If Text starts with an UTF-8 BOM, the BOM is removed and true will + * be returned. + *} +function CheckReplaceUTF8BOM(var Text: RawByteString): boolean; -function RecodeString(const Src: string; SrcEncoding: TEncoding): WideString; +{** + * Parses an encoding string to its TEncoding equivalent. + * Surrounding whitespace and dashes ('-') are removed, the upper-cased + * resulting value is then compared with TEncodingNames. + * If the encoding was not found, the result is set to the Default encoding. + *} +function ParseEncoding(const EncodingStr: AnsiString; Default: TEncoding): TEncoding; + +{** + * Returns the name of an encoding. + *} +function EncodingName(Encoding: TEncoding): AnsiString; implementation +uses + StrUtils, + pcre, + ULog; + type - TConversionTable = array[0..127] of WideChar; + IEncoder = interface + function GetName(): AnsiString; + function Encode(const InStr: UCS4String; out OutStr: RawByteString): boolean; + function Decode(const InStr: RawByteString; out OutStr: UCS4String): boolean; + end; + + TEncoder = class(TInterfacedObject, IEncoder) + public + function GetName(): AnsiString; virtual; abstract; + function Encode(const InStr: UCS4String; out OutStr: RawByteString): boolean; virtual; abstract; + function Decode(const InStr: RawByteString; out OutStr: UCS4String): boolean; virtual; abstract; + end; + + TSingleByteEncoder = class(TEncoder) + public + function Encode(const InStr: UCS4String; out OutStr: RawByteString): boolean; override; + function Decode(const InStr: RawByteString; out OutStr: UCS4String): boolean; override; + function DecodeChar(InChr: AnsiChar; out OutChr: UCS4Char): boolean; virtual; abstract; + function EncodeChar(InChr: UCS4Char; out OutChr: AnsiChar): boolean; virtual; abstract; + end; const - // Windows-1250 Central/Eastern Europe (used by Ultrastar) - CP1250Table: TConversionTable = ( - { $80 } - #$20AC, #0, #$201A, #0, #$201E, #$2026, #$2020, #$2021, - #0, #$2030, #$0160, #$2039, #$015A, #$0164, #$017D, #$0179, - { $90 } - #0, #$2018, #$2019, #$201C, #$201D, #$2022, #$2013, #$2014, - #0, #$2122, #$0161, #$203A, #$015B, #$0165, #$017E, #$017A, - { $A0 } - #$00A0, #$02C7, #$02D8, #$0141, #$00A4, #$0104, #$00A6, #$00A7, - #$00A8, #$00A9, #$015E, #$00AB, #$00AC, #$00AD, #$00AE, #$017B, - { $B0 } - #$00B0, #$00B1, #$02DB, #$0142, #$00B4, #$00B5, #$00B6, #$00B7, - #$00B8, #$0105, #$015F, #$00BB, #$013D, #$02DD, #$013E, #$017C, - { $C0 } - #$0154, #$00C1, #$00C2, #$0102, #$00C4, #$0139, #$0106, #$00C7, - #$010C, #$00C9, #$0118, #$00CB, #$011A, #$00CD, #$00CE, #$010E, - { $D0 } - #$0110, #$0143, #$0147, #$00D3, #$00D4, #$0150, #$00D6, #$00D7, - #$0158, #$016E, #$00DA, #$0170, #$00DC, #$00DD, #$0162, #$00DF, - { $E0 } - #$0155, #$00E1, #$00E2, #$0103, #$00E4, #$013A, #$0107, #$00E7, - #$010D, #$00E9, #$0119, #$00EB, #$011B, #$00ED, #$00EE, #$010F, - { $F0 } - #$0111, #$0144, #$0148, #$00F3, #$00F4, #$0151, #$00F6, #$00F7, - #$0159, #$016F, #$00FA, #$0171, #$00FC, #$00FD, #$0163, #$02D9 - ); + ERROR_CHAR = '?'; - // Windows-1252 Western Europe (used by UltraStar Deluxe < 1.1) - CP1252Table: TConversionTable = ( - { $80 } - #$20AC, #0, #$201A, #$0192, #$201E, #$2026, #$2020, #$2021, - #$02C6, #$2030, #$0160, #$2039, #$0152, #0, #$017D, #0, - { $90 } - #0, #$2018, #$2019, #$201C, #$201D, #$2022, #$2013, #$2014, - #$02DC, #$2122, #$0161, #$203A, #$0153, #0, #$017E, #$0178, - { $A0 } - #$00A0, #$00A1, #$00A2, #$00A3, #$00A4, #$00A5, #$00A6, #$00A7, - #$00A8, #$00A9, #$00AA, #$00AB, #$00AC, #$00AD, #$00AE, #$00AF, - { $B0 } - #$00B0, #$00B1, #$00B2, #$00B3, #$00B4, #$00B5, #$00B6, #$00B7, - #$00B8, #$00B9, #$00BA, #$00BB, #$00BC, #$00BD, #$00BE, #$00BF, - { $C0 } - #$00C0, #$00C1, #$00C2, #$00C3, #$00C4, #$00C5, #$00C6, #$00C7, - #$00C8, #$00C9, #$00CA, #$00CB, #$00CC, #$00CD, #$00CE, #$00CF, - { $D0 } - #$00D0, #$00D1, #$00D2, #$00D3, #$00D4, #$00D5, #$00D6, #$00D7, - #$00D8, #$00D9, #$00DA, #$00DB, #$00DC, #$00DD, #$00DE, #$00DF, - { $E0 } - #$00E0, #$00E1, #$00E2, #$00E3, #$00E4, #$00E5, #$00E6, #$00E7, - #$00E8, #$00E9, #$00EA, #$00EB, #$00EC, #$00ED, #$00EE, #$00EF, - { $F0 } - #$00F0, #$00F1, #$00F2, #$00F3, #$00F4, #$00F5, #$00F6, #$00F7, - #$00F8, #$00F9, #$00FA, #$00FB, #$00FC, #$00FD, #$00FE, #$00FF - ); +var + Encoders: array[TEncoding] of IEncoder; +function TSingleByteEncoder.Encode(const InStr: UCS4String; out OutStr: RawByteString): boolean; +var + I: integer; +begin + SetLength(OutStr, LengthUCS4(InStr)); + Result := true; + for I := 1 to Length(OutStr) do + begin + if (not EncodeChar(InStr[I-1], OutStr[I])) then + Result := false; + end; +end; -function Convert(const Src: string; const Table: TConversionTable): WideString; +function TSingleByteEncoder.Decode(const InStr: RawByteString; out OutStr: UCS4String): boolean; var - SrcPos, DstPos: integer; + I: integer; begin - SetLength(Result, Length(Src)); - DstPos := 1; - for SrcPos := 1 to Length(Src) do + SetLength(OutStr, Length(InStr)+1); + Result := true; + for I := 1 to Length(InStr) do begin - if (Src[SrcPos] < #128) then - begin - // copy ASCII char - Result[DstPos] := Src[SrcPos]; - Inc(DstPos); - end - else + if (not DecodeChar(InStr[I], OutStr[I-1])) then + Result := false; + end; + OutStr[High(OutStr)] := 0; +end; + +function DecodeString(const Src: RawByteString; out Dst: WideString; SrcEncoding: TEncoding): boolean; +var + DstUCS4: UCS4String; +begin + Result := Encoders[SrcEncoding].Decode(Src, DstUCS4); + Dst := UCS4StringToWideString(DstUCS4); +end; + +function DecodeString(const Src: RawByteString; SrcEncoding: TEncoding): WideString; +begin + DecodeString(Src, Result, SrcEncoding); +end; + +function DecodeStringUTF8(const Src: RawByteString; out Dst: UTF8String; SrcEncoding: TEncoding): boolean; +var + DstUCS4: UCS4String; +begin + Result := Encoders[SrcEncoding].Decode(Src, DstUCS4); + Dst := UCS4ToUTF8String(DstUCS4); +end; + +function DecodeStringUTF8(const Src: RawByteString; SrcEncoding: TEncoding): UTF8String; +begin + DecodeStringUTF8(Src, Result, SrcEncoding); +end; + +function EncodeString(const Src: WideString; out Dst: RawByteString; DstEncoding: TEncoding): boolean; +begin + Result := Encoders[DstEncoding].Encode(WideStringToUCS4String(Src), Dst); +end; + +function EncodeString(const Src: WideString; DstEncoding: TEncoding): RawByteString; +begin + EncodeString(Src, Result, DstEncoding); +end; + +function EncodeStringUTF8(const Src: UTF8String; out Dst: RawByteString; DstEncoding: TEncoding): boolean; +begin + Result := Encoders[DstEncoding].Encode(UTF8ToUCS4String(Src), Dst); +end; + +function EncodeStringUTF8(const Src: UTF8String; DstEncoding: TEncoding): RawByteString; +begin + EncodeStringUTF8(Src, Result, DstEncoding); +end; + +function CheckReplaceUTF8BOM(var Text: RawByteString): boolean; +begin + if AnsiStartsStr(UTF8_BOM, Text) then + begin + Text := Copy(Text, Length(UTF8_BOM)+1, Length(Text)-Length(UTF8_BOM)); + Result := true; + Exit; + end; + Result := false; +end; + +function ParseEncoding(const EncodingStr: AnsiString; Default: TEncoding): TEncoding; +var + PrepStr: AnsiString; // prepared encoding string + Encoding: TEncoding; +begin + // remove surrounding whitespace, replace dashes, to upper case + PrepStr := UpperCase(AnsiReplaceStr(Trim(EncodingStr), '-', '')); + for Encoding := Low(TEncoding) to High(TEncoding) do + begin + if (Encoders[Encoding].GetName() = PrepStr) then begin - // look-up char - Result[DstPos] := Table[Ord(Src[SrcPos]) - 128]; - // ignore invalid characters - if (Result[DstPos] <> #0) then - Inc(DstPos); + Result := Encoding; + Exit; end; end; - SetLength(Result, DstPos-1); + Result := Default; end; -function RecodeString(const Src: string; SrcEncoding: TEncoding): WideString; +function EncodingName(Encoding: TEncoding): AnsiString; begin - case SrcEncoding of - encCP1250: - Result := Convert(Src, CP1250Table); - encCP1252: - Result := Convert(Src, CP1252Table); - encUTF8: - Result := UTF8Decode(Src); - encNative: - Result := UTF8Decode(AnsiToUtf8(Src)); - end; + Result := Encoders[Encoding].GetName(); end; +{$I ..\\encoding\\Locale.inc} +{$I ..\\encoding\\UTF8.inc} +{$I ..\\encoding\\CP1250.inc} +{$I ..\\encoding\\CP1252.inc} +{$I ..\\encoding\\Auto.inc} + +initialization + Encoders[encLocale] := TEncoderLocale.Create; + Encoders[encUTF8] := TEncoderUTF8.Create; + Encoders[encCP1250] := TEncoderCP1250.Create; + Encoders[encCP1252] := TEncoderCP1252.Create; + + // use USDX < 1.1 encoding for backward compatibility (encCP1252) + Encoders[encAuto] := TEncoderAuto.Create(Encoders[encUTF8], Encoders[encCP1252]); + end. diff --git a/Lua/src/base/UTexture.pas b/Lua/src/base/UTexture.pas index 4f33b78a..e477dbb1 100644 --- a/Lua/src/base/UTexture.pas +++ b/Lua/src/base/UTexture.pas @@ -40,6 +40,7 @@ uses Classes, SysUtils, UCommon, + UPath, SDL, SDL_Image; @@ -66,7 +67,7 @@ type TexX2: real; TexY2: real; Alpha: real; - Name: string; // experimental for handling cache images. maybe it's useful for dynamic skins + Name: IPath; // experimental for handling cache images. maybe it's useful for dynamic skins end; type @@ -91,9 +92,9 @@ procedure AdjustPixelFormat(var TexSurface: PSDL_Surface; Typ: TTextureType); type PTextureEntry = ^TTextureEntry; TTextureEntry = record - Name: string; + Name: IPath; Typ: TTextureType; - Color: Cardinal; + Color: cardinal; // we use normal TTexture, it's easier to implement and if needed - we copy ready data Texture: TTexture; // Full-size texture @@ -104,8 +105,8 @@ type private Texture: array of TTextureEntry; public - procedure AddTexture(var Tex: TTexture; Typ: TTextureType; Color: Cardinal; Cache: boolean); - function FindTexture(const Name: string; Typ: TTextureType; Color: Cardinal): integer; + procedure AddTexture(var Tex: TTexture; Typ: TTextureType; Color: cardinal; Cache: boolean); + function FindTexture(const Name: IPath; Typ: TTextureType; Color: cardinal): integer; end; TTextureUnit = class @@ -115,15 +116,15 @@ type Limit: integer; procedure AddTexture(var Tex: TTexture; Typ: TTextureType; Cache: boolean = false); overload; - procedure AddTexture(var Tex: TTexture; Typ: TTextureType; Color: Cardinal; Cache: boolean = false); overload; - function GetTexture(const Name: string; Typ: TTextureType; FromCache: boolean = false): TTexture; overload; - function GetTexture(const Name: string; Typ: TTextureType; Col: LongWord; FromCache: boolean = false): TTexture; overload; - function LoadTexture(FromRegistry: boolean; const Identifier: string; Typ: TTextureType; Col: LongWord): TTexture; overload; - function LoadTexture(const Identifier: string; Typ: TTextureType; Col: LongWord): TTexture; overload; - function LoadTexture(const Identifier: string): TTexture; overload; - function CreateTexture(Data: PChar; const Name: string; Width, Height: word; BitsPerPixel: byte): TTexture; - procedure UnloadTexture(const Name: string; Typ: TTextureType; FromCache: boolean); overload; - procedure UnloadTexture(const Name: string; Typ: TTextureType; Col: Cardinal; FromCache: boolean); overload; + procedure AddTexture(var Tex: TTexture; Typ: TTextureType; Color: cardinal; Cache: boolean = false); overload; + function GetTexture(const Name: IPath; Typ: TTextureType; FromCache: boolean = false): TTexture; overload; + function GetTexture(const Name: IPath; Typ: TTextureType; Col: LongWord; FromCache: boolean = false): TTexture; overload; + function LoadTexture(FromRegistry: boolean; const Identifier: IPath; Typ: TTextureType; Col: LongWord): TTexture; overload; + function LoadTexture(const Identifier: IPath; Typ: TTextureType; Col: LongWord): TTexture; overload; + function LoadTexture(const Identifier: IPath): TTexture; overload; + function CreateTexture(Data: PChar; const Name: IPath; Width, Height: word; BitsPerPixel: byte): TTexture; + procedure UnloadTexture(const Name: IPath; Typ: TTextureType; FromCache: boolean); overload; + procedure UnloadTexture(const Name: IPath; Typ: TTextureType; Col: cardinal; FromCache: boolean); overload; //procedure FlushTextureDatabase(); constructor Create; @@ -164,10 +165,10 @@ begin SDL_FreeSurface(TempSurface); end; end; - + { TTextureDatabase } -procedure TTextureDatabase.AddTexture(var Tex: TTexture; Typ: TTextureType; Color: Cardinal; Cache: boolean); +procedure TTextureDatabase.AddTexture(var Tex: TTexture; Typ: TTextureType; Color: cardinal; Cache: boolean); var TextureIndex: integer; begin @@ -188,7 +189,7 @@ begin Texture[TextureIndex].Texture := Tex; end; -function TTextureDatabase.FindTexture(const Name: string; Typ: TTextureType; Color: Cardinal): integer; +function TTextureDatabase.FindTexture(const Name: IPath; Typ: TTextureType; Color: cardinal): integer; var TextureIndex: integer; CurrentTexture: PTextureEntry; @@ -197,7 +198,7 @@ begin for TextureIndex := 0 to High(Texture) do begin CurrentTexture := @Texture[TextureIndex]; - if (CurrentTexture.Name = Name) and + if (CurrentTexture.Name.Equals(Name)) and (CurrentTexture.Typ = Typ) then begin // colorized textures must match in their color too @@ -211,7 +212,6 @@ begin end; end; - { TTextureUnit } constructor TTextureUnit.Create; @@ -226,33 +226,32 @@ begin inherited Destroy; end; - procedure TTextureUnit.AddTexture(var Tex: TTexture; Typ: TTextureType; Cache: boolean); begin TextureDatabase.AddTexture(Tex, Typ, 0, Cache); end; -procedure TTextureUnit.AddTexture(var Tex: TTexture; Typ: TTextureType; Color: Cardinal; Cache: boolean); +procedure TTextureUnit.AddTexture(var Tex: TTexture; Typ: TTextureType; Color: cardinal; Cache: boolean); begin TextureDatabase.AddTexture(Tex, Typ, Color, Cache); end; -function TTextureUnit.LoadTexture(FromRegistry: boolean; const Identifier: string; Typ: TTextureType; Col: LongWord): TTexture; +function TTextureUnit.LoadTexture(FromRegistry: boolean; const Identifier: IPath; Typ: TTextureType; Col: LongWord): TTexture; begin // FIXME: what is the FromRegistry parameter supposed to do? Result := LoadTexture(Identifier, Typ, Col); end; -function TTextureUnit.LoadTexture(const Identifier: string): TTexture; +function TTextureUnit.LoadTexture(const Identifier: IPath): TTexture; begin Result := LoadTexture(Identifier, TEXTURE_TYPE_PLAIN, 0); end; -function TTextureUnit.LoadTexture(const Identifier: string; Typ: TTextureType; Col: LongWord): TTexture; +function TTextureUnit.LoadTexture(const Identifier: IPath; Typ: TTextureType; Col: LongWord): TTexture; var TexSurface: PSDL_Surface; - newWidth, newHeight: Cardinal; - oldWidth, oldHeight: Cardinal; + newWidth, newHeight: integer; + oldWidth, oldHeight: integer; ActTex: GLuint; begin // zero texture data @@ -262,7 +261,7 @@ begin TexSurface := LoadImage(Identifier); if not assigned(TexSurface) then begin - Log.LogError('Could not load texture: "' + Identifier +'" with type "'+ TextureTypeToStr(Typ) +'"', + Log.LogError('Could not load texture: "' + Identifier.ToNative +'" with type "'+ TextureTypeToStr(Typ) +'"', 'TTextureUnit.LoadTexture'); Exit; end; @@ -338,8 +337,8 @@ begin X := 0; Y := 0; Z := 0; - W := 0; - H := 0; + W := oldWidth; + H := oldHeight; ScaleW := 1; ScaleH := 1; Rot := 0; @@ -365,16 +364,16 @@ begin SDL_FreeSurface(TexSurface); end; -function TTextureUnit.GetTexture(const Name: string; Typ: TTextureType; FromCache: boolean): TTexture; +function TTextureUnit.GetTexture(const Name: IPath; Typ: TTextureType; FromCache: boolean): TTexture; begin Result := GetTexture(Name, Typ, 0, FromCache); end; -function TTextureUnit.GetTexture(const Name: string; Typ: TTextureType; Col: LongWord; FromCache: boolean): TTexture; +function TTextureUnit.GetTexture(const Name: IPath; Typ: TTextureType; Col: LongWord; FromCache: boolean): TTexture; var TextureIndex: integer; begin - if (Name = '') then + if (Name.IsUnset) then begin // zero texture data FillChar(Result, SizeOf(Result), 0); @@ -415,7 +414,7 @@ begin Result := TextureDatabase.Texture[TextureIndex].Texture; end; -function TTextureUnit.CreateTexture(Data: PChar; const Name: string; Width, Height: word; BitsPerPixel: byte): TTexture; +function TTextureUnit.CreateTexture(Data: PChar; const Name: IPath; Width, Height: word; BitsPerPixel: byte): TTexture; var //Error: integer; ActTex: GLuint; @@ -431,8 +430,8 @@ begin {$ELSE} glTexImage2D(GL_TEXTURE_2D, 0, 3, Width, Height, 0, GL_RGB, GL_UNSIGNED_BYTE, Data); {$ENDIF} - - { + +{ if Mipmapping then begin Error := gluBuild2DMipmaps(GL_TEXTURE_2D, 3, W, H, GL_RGB, GL_UNSIGNED_BYTE, @Data[0]); @@ -440,8 +439,8 @@ begin if Error > 0 then Log.LogError('gluBuild2DMipmaps() failed', 'TTextureUnit.CreateTexture'); end; - } - +} + Result.X := 0; Result.Y := 0; Result.Z := 0; @@ -469,19 +468,19 @@ begin Result.Name := Name; end; -procedure TTextureUnit.UnloadTexture(const Name: string; Typ: TTextureType; FromCache: boolean); +procedure TTextureUnit.UnloadTexture(const Name: IPath; Typ: TTextureType; FromCache: boolean); begin UnloadTexture(Name, Typ, 0, FromCache); end; -procedure TTextureUnit.UnloadTexture(const Name: string; Typ: TTextureType; Col: Cardinal; FromCache: boolean); +procedure TTextureUnit.UnloadTexture(const Name: IPath; Typ: TTextureType; Col: cardinal; FromCache: boolean); var T: integer; TexNum: GLuint; begin T := TextureDatabase.FindTexture(Name, Typ, Col); - if not FromCache then + if not FromCache then begin TexNum := TextureDatabase.Texture[T].Texture.TexNum; if TexNum > 0 then @@ -529,20 +528,20 @@ end; function ParseTextureType(const TypeStr: string; Default: TTextureType): TTextureType; var - TexType: TTextureType; + TextureType: TTextureType; UpCaseStr: string; begin UpCaseStr := UpperCase(TypeStr); - for TexType := Low(TextureTypeStr) to High(TextureTypeStr) do + for TextureType := Low(TextureTypeStr) to High(TextureTypeStr) do begin - if (UpCaseStr = UpperCase(TextureTypeStr[TexType])) then + if (UpCaseStr = UpperCase(TextureTypeStr[TextureType])) then begin - Result := TexType; + Result := TextureType; Exit; end; end; - Log.LogWarn('Unknown texture-type: "' + TypeStr + '"', 'ParseTextureType'); - Result := TEXTURE_TYPE_PLAIN; + Log.LogWarn('Unknown texture type: "' + TypeStr + '". Using default texture type "' + TextureTypeToStr(Default) + '"', 'ParseTextureType'); + Result := Default; end; end. diff --git a/Lua/src/base/UThemes.pas b/Lua/src/base/UThemes.pas index 9bf858ed..4322815e 100644 --- a/Lua/src/base/UThemes.pas +++ b/Lua/src/base/UThemes.pas @@ -34,21 +34,22 @@ interface {$I switches.inc} uses - ULog, IniFiles, SysUtils, Classes, - UTexture; + ULog, + UTexture, + UPath; type TRGB = record - R: single; - G: single; - B: single; + R: single; + G: single; + B: single; end; TRGBA = record - R, G, B, A: Double; + R, G, B, A: double; end; type @@ -112,7 +113,7 @@ type Font: integer; Size: integer; Align: integer; - Text: string; + Text: UTF8String; //Reflection Reflection: boolean; ReflectionSpacing: real; @@ -175,13 +176,14 @@ type W: integer; H: integer; Z: real; + SBGW: integer; TextSize: integer; - //SBGW Mod - SBGW: integer; + showArrows:boolean; + oneItemOnly:boolean; - Text: string; + Text: UTF8String; ColR, ColG, ColB, Int: real; DColR, DColG, DColB, DInt: real; TColR, TColG, TColB, TInt: real; @@ -235,8 +237,8 @@ type TextDescription: TThemeText; TextDescriptionLong: TThemeText; - Description: array[0..5] of string; - DescriptionLong: array[0..5] of string; + Description: array[0..5] of UTF8String; + DescriptionLong: array[0..5] of UTF8String; end; TThemeName = class(TThemeBasic) @@ -353,12 +355,17 @@ type TextP3RScore: TThemeText; //Linebonus Translations - LineBonusText: array [0..8] of string; + LineBonusText: array [0..8] of UTF8String; //Pause Popup PausePopUp: TThemeStatic; end; + TThemeLyricBar = record + IndicatorYOffset, UpperX, UpperW, UpperY, UpperH, + LowerX, LowerW, LowerY, LowerH : integer; + end; + TThemeScore = class(TThemeBasic) TextArtist: TThemeText; TextTitle: TThemeText; @@ -402,6 +409,7 @@ type TextNumber: AThemeText; TextName: AThemeText; TextScore: AThemeText; + TextDate: AThemeText; end; TThemeOptions = class(TThemeBasic) @@ -415,7 +423,7 @@ type ButtonExit: TThemeButton; TextDescription: TThemeText; - Description: array[0..7] of string; + Description: array[0..7] of UTF8String; end; TThemeOptionsGame = class(TThemeBasic) @@ -490,8 +498,8 @@ type TextDescription: TThemeText; TextDescriptionLong: TThemeText; - Description: array[0..5] of string; - DescriptionLong: array[0..5] of string; + Description: array[0..5] of UTF8string; + DescriptionLong: array[0..5] of UTF8string; end; //Error- and Check-Popup @@ -525,10 +533,10 @@ type TextFound: TThemeText; //Translated Texts - Songsfound: string; - NoSongsfound: string; - CatText: string; - IType: array [0..2] of string; + Songsfound: UTF8String; + NoSongsfound: UTF8String; + CatText: UTF8String; + IType: array [0..2] of UTF8String; end; //Party Screens @@ -694,15 +702,15 @@ type TextPage: TThemeText; TextList: AThemeText; - Description: array[0..3] of string; - DescriptionR: array[0..3] of string; - FormatStr: array[0..3] of string; - PageStr: string; + Description: array[0..3] of UTF8String; + DescriptionR: array[0..3] of UTF8String; + FormatStr: array[0..3] of UTF8String; + PageStr: UTF8String; end; //Playlist Translations TThemePlaylist = record - CatText: string; + CatText: UTF8String; end; TTheme = class @@ -723,6 +731,7 @@ type Level: TThemeLevel; Song: TThemeSong; Sing: TThemeSing; + LyricBar: TThemeLyricBar; Score: TThemeScore; Top5: TThemeTop5; Options: TThemeOptions; @@ -754,11 +763,11 @@ type Playlist: TThemePlaylist; - ILevel: array[0..2] of string; + ILevel: array[0..2] of UTF8String; - constructor Create(const FileName: string); overload; // Initialize theme system - constructor Create(const FileName: string; Color: integer); overload; // Initialize theme system with color - function LoadTheme(FileName: string; sColor: integer): boolean; // Load some theme settings from file + constructor Create(const FileName: IPath); overload; // Initialize theme system + constructor Create(const FileName: IPath; Color: integer); overload; // Initialize theme system with color + function LoadTheme(const FileName: IPath; sColor: integer): boolean; // Load some theme settings from file procedure LoadColors; @@ -838,12 +847,12 @@ begin glColor4f(Color.R, Color.G, Color.B, Min(Color.A, Alpha)); end; -constructor TTheme.Create(const FileName: string); +constructor TTheme.Create(const FileName: IPath); begin Create(FileName, 0); end; -constructor TTheme.Create(const FileName: string; Color: integer); +constructor TTheme.Create(const FileName: IPath; Color: integer); begin inherited Create(); @@ -886,7 +895,7 @@ begin end; -function TTheme.LoadTheme(FileName: string; sColor: integer): boolean; +function TTheme.LoadTheme(const FileName: IPath; sColor: integer): boolean; var I: integer; begin @@ -894,23 +903,21 @@ begin CreateThemeObjects(); - Log.LogStatus('Loading: '+ FileName, 'TTheme.LoadTheme'); - - FileName := AdaptFilePaths(FileName); + Log.LogStatus('Loading: '+ FileName.ToNative, 'TTheme.LoadTheme'); - if not FileExists(FileName) then + if not FileName.IsFile() then begin - Log.LogError('Theme does not exist ('+ FileName +')', 'TTheme.LoadTheme'); + Log.LogError('Theme does not exist ('+ FileName.ToNative +')', 'TTheme.LoadTheme'); end; - if FileExists(FileName) then + if FileName.IsFile() then begin Result := true; {$IFDEF THEMESAVE} - ThemeIni := TIniFile.Create(FileName); + ThemeIni := TIniFile.Create(FileName.ToNative); {$ELSE} - ThemeIni := TMemIniFile.Create(FileName); + ThemeIni := TMemIniFile.Create(FileName.ToNative); {$ENDIF} if ThemeIni.ReadString('Theme', 'Name', '') <> '' then @@ -1031,9 +1038,19 @@ begin ThemeLoadStatic(Song.StaticTeam3Joker5, 'SongStaticTeam3Joker5'); + //LyricBar asd + LyricBar.UpperX := ThemeIni.ReadInteger('SingLyricsUpperBar', 'X', 0); + LyricBar.UpperW := ThemeIni.ReadInteger('SingLyricsUpperBar', 'W', 0); + LyricBar.UpperY := ThemeIni.ReadInteger('SingLyricsUpperBar', 'Y', 0); + LyricBar.UpperH := ThemeIni.ReadInteger('SingLyricsUpperBar', 'H', 0); + LyricBar.IndicatorYOffset := ThemeIni.ReadInteger('SingLyricsUpperBar', 'IndicatorYOffset', 0); + LyricBar.LowerX := ThemeIni.ReadInteger('SingLyricsLowerBar', 'X', 0); + LyricBar.LowerW := ThemeIni.ReadInteger('SingLyricsLowerBar', 'W', 0); + LyricBar.LowerY := ThemeIni.ReadInteger('SingLyricsLowerBar', 'Y', 0); + LyricBar.LowerH := ThemeIni.ReadInteger('SingLyricsLowerBar', 'H', 0); + // Sing ThemeLoadBasic(Sing, 'Sing'); - //TimeBar mod ThemeLoadStatic(Sing.StaticTimeProgress, 'SingTimeProgress'); ThemeLoadText(Sing.TextTimeText, 'SingTimeText'); @@ -1160,6 +1177,7 @@ begin ThemeLoadTexts(Top5.TextNumber, 'Top5TextNumber'); ThemeLoadTexts(Top5.TextName, 'Top5TextName'); ThemeLoadTexts(Top5.TextScore, 'Top5TextScore'); + ThemeLoadTexts(Top5.TextDate, 'Top5TextDate'); // Options ThemeLoadBasic(Options, 'Options'); @@ -1769,7 +1787,7 @@ begin ThemeSelectS.SkipX := ThemeIni.ReadInteger(Name, 'SkipX', 0); - ThemeSelectS.SBGW := ThemeIni.ReadInteger(Name, 'SBGW', 450); + ThemeSelectS.SBGW := ThemeIni.ReadInteger(Name, 'SBGW', 400); LoadColor(ThemeSelectS.ColR, ThemeSelectS.ColG, ThemeSelectS.ColB, ThemeIni.ReadString(Name, 'Color', '')); ThemeSelectS.Int := ThemeIni.ReadFloat(Name, 'Int', 1); diff --git a/Lua/src/base/UTime.pas b/Lua/src/base/UTime.pas index 3f35dffd..83844cb5 100644 --- a/Lua/src/base/UTime.pas +++ b/Lua/src/base/UTime.pas @@ -61,20 +61,20 @@ procedure CountSkipTime; procedure CountMidTime; var - USTime : TTime; + USTime: TTime; VideoBGTimer: TRelativeTimer; - TimeNew : int64; - TimeOld : int64; - TimeSkip : real; - TimeMid : real; - TimeMidTemp : int64; + TimeNew: int64; + TimeOld: int64; + TimeSkip: real; + TimeMid: real; + TimeMidTemp: int64; implementation uses sdl, - ucommon; + UCommon; const cSDLCorrectionRatio = 1000; @@ -91,14 +91,14 @@ http://www.gamedev.net/community/forums/topic.asp?topic_id=466145&whichpage=1%EE procedure CountSkipTimeSet; begin - TimeNew := SDL_GetTicks(); + TimeNew := SDL_GetTicks(); end; procedure CountSkipTime; begin - TimeOld := TimeNew; - TimeNew := SDL_GetTicks(); - TimeSkip := (TimeNew-TimeOld) / cSDLCorrectionRatio; + TimeOld := TimeNew; + TimeNew := SDL_GetTicks(); + TimeSkip := (TimeNew-TimeOld) / cSDLCorrectionRatio; end; procedure CountMidTime; @@ -127,10 +127,10 @@ end; **} (* - * Creates a new timer. - * If TriggerMode is false (default), the timer + * creates a new timer. + * if triggermode is false (default), the timer * will immediately begin with counting. - * If TriggerMode is true, it will wait until Get/SetTime() or Pause() is called + * if triggermode is true, it will wait until get/settime() or pause() is called * for the first time. *) constructor TRelativeTimer.Create(TriggerMode: boolean); diff --git a/Lua/src/base/UUnicodeUtils.pas b/Lua/src/base/UUnicodeUtils.pas new file mode 100644 index 00000000..37b53a67 --- /dev/null +++ b/Lua/src/base/UUnicodeUtils.pas @@ -0,0 +1,670 @@ +{* 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 UUnicodeUtils; + +interface + +{$IFDEF FPC} + {$MODE Delphi} +{$ENDIF} + +uses +{$IFDEF MSWINDOWS} + Windows, +{$ENDIF} + StrUtils, + SysUtils; + +type + // String with unknown encoding. Introduced with Delphi 2009 and maybe soon + // with FPC. + RawByteString = AnsiString; + +{** + * Returns true if the system uses UTF-8 as default string type + * (filesystem or API calls). + * This is always true on Mac OS X and always false on Win32. On Unix it depends + * on the LC_CTYPE setting. + * Do not use AnsiToUTF8() or UTF8ToAnsi() if this function returns true. + *} +function IsNativeUTF8(): boolean; + +(* + * Character classes + *) + +function IsAlphaChar(ch: WideChar): boolean; overload; +function IsAlphaChar(ch: UCS4Char): boolean; overload; + +function IsNumericChar(ch: WideChar): boolean; overload; +function IsNumericChar(ch: UCS4Char): boolean; overload; + +function IsAlphaNumericChar(ch: WideChar): boolean; overload; +function IsAlphaNumericChar(ch: UCS4Char): boolean; overload; + +function IsPunctuationChar(ch: WideChar): boolean; overload; +function IsPunctuationChar(ch: UCS4Char): boolean; overload; + +function IsControlChar(ch: WideChar): boolean; overload; +function IsControlChar(ch: UCS4Char): boolean; overload; + +function IsPrintableChar(ch: WideChar): boolean; overload; +function IsPrintableChar(ch: UCS4Char): boolean; overload; + +{** + * Checks if the given string is a valid UTF-8 string. + * If an ANSI encoded string (with char codes >= 128) is passed, the + * function will most probably return false, as most ANSI strings sequences + * are illegal in UTF-8. + *} +function IsUTF8String(const str: RawByteString): boolean; + +{** + * Iterates over an UTF-8 encoded string. + * StrPtr will be increased to the beginning of the next character on each + * call. + * Results true if the given string starts with an UTF-8 encoded char. + *} +function NextCharUTF8(var StrPtr: PAnsiChar; out Ch: UCS4Char): boolean; + +{** + * Deletes Count chars (not bytes) beginning at char- (not byte-) position Index. + * Index values start with 1. + *} +procedure UTF8Delete(var Str: UTF8String; Index: Integer; Count: Integer); +procedure UCS4Delete(var Str: UCS4String; Index: Integer; Count: Integer); + +{** + * Checks if the string is composed of ASCII characters. + *} +function IsASCIIString(const str: RawByteString): boolean; + +{* + * String format conversion + *} + +function UTF8ToUCS4String(const str: UTF8String): UCS4String; +function UCS4ToUTF8String(const str: UCS4String): UTF8String; overload; +function UCS4ToUTF8String(ch: UCS4Char): UTF8String; overload; + +{** + * Returns the number of characters (not bytes) in string str. + *} +function LengthUTF8(const str: UTF8String): integer; + +{** + * Returns the length of an UCS4String. Note that Length(UCS4String) returns + * the length+1 as UCS4Strings are zero-terminated. + *} +function LengthUCS4(const str: UCS4String): integer; + +{** @seealso WideCompareStr *} +function UTF8CompareStr(const S1, S2: UTF8String): integer; +{** @seealso WideCompareText *} +function UTF8CompareText(const S1, S2: UTF8String): integer; + +function UTF8StartsText(const SubText, Text: UTF8String): boolean; + +function UTF8ContainsStr(const Text, SubText: UTF8String): boolean; +function UTF8ContainsText(const Text, SubText: UTF8String): boolean; + +{** @seealso WideUpperCase *} +function UTF8UpperCase(const str: UTF8String): UTF8String; +{** @seealso WideCompareText *} +function UTF8LowerCase(const str: UTF8String): UTF8String; + +{** + * Converts a UCS-4 char ch to its upper-case representation. + *} +function UCS4UpperCase(ch: UCS4Char): UCS4Char; overload; + +{** + * Converts a UCS-4 string str to its upper-case representation. + *} +function UCS4UpperCase(const str: UCS4String): UCS4String; overload; + +{** + * Converts a UCS4Char to an UCS4String. + * Note that UCS4Strings are zero-terminated dynamic arrays. + *} +function UCS4CharToString(ch: UCS4Char): UCS4String; + +{** + * @seealso System.Pos() + *} +function UTF8Pos(const substr: UTF8String; const str: UTF8String): Integer; + +{** + * Copies a segment of str starting with Index (1-based) with Count characters (not bytes). + *} +function UTF8Copy(const str: UTF8String; Index: Integer = 1; Count: Integer = -1): UTF8String; + +{** + * Copies a segment of str starting with Index (0-based) with Count characters. + * Note: Do not use Copy() to copy UCS4Strings as the result will not contain + * a trailing #0 character and hence is invalid. + *} +function UCS4Copy(const str: UCS4String; Index: Integer = 0; Count: Integer = -1): UCS4String; + +(* + * Converts a WideString to its upper- or lower-case representation. + * Wrapper for WideUpper/LowerCase. Needed because some plattforms have + * problems with unicode support. + * + * Note that characters in UTF-16 might consist of one or two WideChar valus + * (see surrogates). So instead of using WideStringUpperCase(ch)[1] for single + * character access, convert to UCS-4 where each character is represented by + * one UCS4Char. + *) +function WideStringUpperCase(const str: WideString) : WideString; overload; +function WideStringUpperCase(ch: WideChar): WideString; overload; +function WideStringLowerCase(const str: WideString): WideString; overload; +function WideStringLowerCase(ch: WideChar): WideString; overload; + +function WideStringReplaceChar(const text: WideString; search, rep: WideChar): WideString; + +implementation + +{$IFDEF UNIX} +{$IFNDEF DARWIN} +const + LC_CTYPE = 0; + +function setlocale(category: integer; locale: PChar): PChar; cdecl; external 'c'; +{$ENDIF} +{$ENDIF} + +var + NativeUTF8: boolean; + +procedure InitUnicodeUtils(); +{$IFDEF UNIX} +{$IFNDEF DARWIN} +var + localeName: PChar; +{$ENDIF} +{$ENDIF} +begin + {$IF Defined(DARWIN)} + NativeUTF8 := true; + {$ELSEIF Defined(MSWindows)} + NativeUTF8 := false; + {$ELSEIF Defined(UNIX)} + // check if locale name contains UTF8 or UTF-8 + localeName := setlocale(LC_CTYPE, nil); + NativeUTF8 := Pos('UTF8', UpperCase(AnsiReplaceStr(localeName, '-', ''))) > 0; + {$ELSE} + raise Exception.Create('Unknown system'); + {$IFEND} +end; + +function IsNativeUTF8(): boolean; +begin + Result := NativeUTF8; +end; + +function IsAlphaChar(ch: WideChar): boolean; +begin + {$IFDEF MSWINDOWS} + Result := IsCharAlphaW(ch); + {$ELSE} + // TODO: add chars > 255 (or replace with libxml2 functions?) + case ch of + 'A'..'Z', // A-Z + 'a'..'z', // a-z + #170,#181,#186, + #192..#214, + #216..#246, + #248..#255: + Result := true; + else + Result := false; + end; + {$ENDIF} +end; + +function IsAlphaChar(ch: UCS4Char): boolean; +begin + Result := IsAlphaChar(WideChar(Ord(ch))); +end; + +function IsNumericChar(ch: WideChar): boolean; +begin + // TODO: replace with libxml2 functions? + // ignore non-arabic numerals as we do not want to handle them + case ch of + '0'..'9': + Result := true; + else + Result := false; + end; +end; + +function IsNumericChar(ch: UCS4Char): boolean; +begin + Result := IsNumericChar(WideChar(Ord(ch))); +end; + +function IsAlphaNumericChar(ch: WideChar): boolean; +begin + Result := (IsAlphaChar(ch) or IsNumericChar(ch)); +end; + +function IsAlphaNumericChar(ch: UCS4Char): boolean; +begin + Result := (IsAlphaChar(ch) or IsNumericChar(ch)); +end; + +function IsPunctuationChar(ch: WideChar): boolean; +begin + // TODO: add chars > 255 (or replace with libxml2 functions?) + case ch of + ' '..'/',':'..'@','['..'`','{'..'~', + #160..#191,#215,#247: + Result := true; + else + Result := false; + end; +end; + +function IsPunctuationChar(ch: UCS4Char): boolean; +begin + Result := IsPunctuationChar(WideChar(Ord(ch))); +end; + +function IsControlChar(ch: WideChar): boolean; +begin + case ch of + #0..#31, + #127..#159: + Result := true; + else + Result := false; + end; +end; + +function IsControlChar(ch: UCS4Char): boolean; +begin + Result := IsControlChar(WideChar(Ord(ch))); +end; + +function IsPrintableChar(ch: WideChar): boolean; +begin + Result := not IsControlChar(ch); +end; + +function IsPrintableChar(ch: UCS4Char): boolean; +begin + Result := IsPrintableChar(WideChar(Ord(ch))); +end; + + +function NextCharUTF8(var StrPtr: PAnsiChar; out Ch: UCS4Char): boolean; + + // find the most significant zero bit (Result: [7..-1]) + function FindZeroMSB(b: byte): integer; + var + Mask: byte; + begin + Mask := $80; + Result := 7; + while (b and Mask <> 0) do + begin + Mask := Mask shr 1; + Dec(Result); + end; + end; + +var + ZeroBit: integer; + SeqCount: integer; // number of trailing bytes to follow +const + Mask: array[1..3] of byte = ($1F, $0F, $07); +begin + Result := false; + SeqCount := 0; + Ch := 0; + + while (StrPtr^ <> #0) do + begin + if (StrPtr^ < #128) then + begin + // check that no more trailing bytes are expected + if (SeqCount = 0) then + begin + Ch := Ord(StrPtr^); + Inc(StrPtr); + Result := true; + end; + Break; + end + else + begin + ZeroBit := FindZeroMSB(Ord(StrPtr^)); + // trailing byte expected + if (SeqCount > 0) then + begin + // check if trailing byte has pattern 10xxxxxx + if (ZeroBit <> 6) then + begin + Inc(StrPtr); + Break; + end; + + Dec(SeqCount); + Ch := (Ch shl 6) or (Ord(StrPtr^) and $3F); + + // check if char is finished + if (SeqCount = 0) then + begin + Inc(StrPtr); + Result := true; + Break; + end; + end + else // leading byte expected + begin + // check if pattern is one of 110xxxxx/1110xxxx/11110xxx + if (ZeroBit > 5) or (ZeroBit < 3) then + begin + Inc(StrPtr); + Break; + end; + // calculate number of trailing bytes (1, 2 or 3) + SeqCount := 6 - ZeroBit; + // extract first part of char + Ch := Ord(StrPtr^) and Mask[SeqCount]; + end; + end; + + Inc(StrPtr); + end; + + if (not Result) then + Ch := Ord('?'); +end; + +function IsUTF8String(const str: RawByteString): boolean; +var + Ch: UCS4Char; + StrPtr: PAnsiChar; +begin + Result := true; + StrPtr := PChar(str); + while (StrPtr^ <> #0) do + begin + if (not NextCharUTF8(StrPtr, Ch)) then + begin + Result := false; + Exit; + end; + end; +end; + +function IsASCIIString(const str: RawByteString): boolean; +var + I: integer; +begin + for I := 1 to Length(str) do + begin + if (str[I] >= #128) then + begin + Result := false; + Exit; + end; + end; + Result := true; +end; + + +function UTF8ToUCS4String(const str: UTF8String): UCS4String; +begin + Result := WideStringToUCS4String(UTF8Decode(str)); +end; + +function UCS4ToUTF8String(const str: UCS4String): UTF8String; +begin + Result := UTF8Encode(UCS4StringToWideString(str)); +end; + +function UCS4ToUTF8String(ch: UCS4Char): UTF8String; +begin + Result := UCS4ToUTF8String(UCS4CharToString(ch)); +end; + +function LengthUTF8(const str: UTF8String): integer; +begin + Result := LengthUCS4(UTF8ToUCS4String(str)); +end; + +function LengthUCS4(const str: UCS4String): integer; +begin + Result := High(str); + if (Result = -1) then + Result := 0; +end; + +function UTF8CompareStr(const S1, S2: UTF8String): integer; +begin + Result := WideCompareStr(UTF8Decode(S1), UTF8Decode(S2)); +end; + +function UTF8CompareText(const S1, S2: UTF8String): integer; +begin + Result := WideCompareText(UTF8Decode(S1), UTF8Decode(S2)); +end; + +function UTF8StartsStr(const SubText, Text: UTF8String): boolean; +begin + // TODO: use WideSameStr (slower but handles different representations of the same char)? + Result := (Pos(SubText, Text) = 1); +end; + +function UTF8StartsText(const SubText, Text: UTF8String): boolean; +begin + // TODO: use WideSameText (slower but handles different representations of the same char)? + Result := (Pos(UTF8UpperCase(SubText), UTF8UpperCase(Text)) = 1); +end; + +function UTF8ContainsStr(const Text, SubText: UTF8String): boolean; +begin + Result := Pos(SubText, Text) > 0; +end; + +function UTF8ContainsText(const Text, SubText: UTF8String): boolean; +begin + Result := Pos(UTF8UpperCase(SubText), UTF8UpperCase(Text)) > 0; +end; + +function UTF8UpperCase(const str: UTF8String): UTF8String; +begin + Result := UTF8Encode(WideStringUpperCase(UTF8Decode(str))); +end; + +function UTF8LowerCase(const str: UTF8String): UTF8String; +begin + Result := UTF8Encode(WideStringLowerCase(UTF8Decode(str))); +end; + +function UCS4UpperCase(ch: UCS4Char): UCS4Char; +begin + Result := UCS4UpperCase(UCS4CharToString(ch))[0]; +end; + +function UCS4UpperCase(const str: UCS4String): UCS4String; +begin + // convert to upper-case as WideString and convert result back to UCS-4 + Result := WideStringToUCS4String( + WideStringUpperCase( + UCS4StringToWideString(str))); +end; + +function UCS4CharToString(ch: UCS4Char): UCS4String; +begin + SetLength(Result, 2); + Result[0] := ch; + Result[1] := 0; +end; + +function UTF8Pos(const substr: UTF8String; const str: UTF8String): Integer; +begin + Result := Pos(substr, str); +end; + +function UTF8Copy(const str: UTF8String; Index: Integer; Count: Integer): UTF8String; +begin + Result := UCS4ToUTF8String(UCS4Copy(UTF8ToUCS4String(str), Index-1, Count)); +end; + +function UCS4Copy(const str: UCS4String; Index: Integer; Count: Integer): UCS4String; +var + I: integer; + MaxCount: integer; +begin + // calculate max. copy count + MaxCount := LengthUCS4(str)-Index; + if (MaxCount < 0) then + MaxCount := 0; + // adjust copy count + if (Count > MaxCount) or (Count < 0) then + Count := MaxCount; + + // copy (and add zero terminator) + SetLength(Result, Count + 1); + for I := 0 to Count-1 do + Result[I] := str[Index+I]; + Result[Count] := 0; +end; + +procedure UTF8Delete(var Str: UTF8String; Index: Integer; Count: Integer); +var + StrUCS4: UCS4String; +begin + StrUCS4 := UTF8ToUCS4String(str); + UCS4Delete(StrUCS4, Index-1, Count); + Str := UCS4ToUTF8String(StrUCS4); +end; + +procedure UCS4Delete(var Str: UCS4String; Index: Integer; Count: Integer); +var + Len: integer; + OldStr: UCS4String; + I: integer; +begin + Len := LengthUCS4(Str); + if (Count <= 0) or (Index < 0) or (Index >= Len) then + Exit; + if (Index + Count > Len) then + Count := Len-Index; + + OldStr := Str; + SetLength(Str, Len-Count+1); + for I := 0 to Index-1 do + Str[I] := OldStr[I]; + for I := Index+Count to Len-1 do + Str[I-Count] := OldStr[I]; + Str[High(Str)] := 0; +end; + +function WideStringUpperCase(ch: WideChar): WideString; +begin + // If WideChar #0 is converted to a WideString in Delphi, a string with + // length 1 and a single char #0 is returned. In FPC an empty (length=0) + // string will be returned. This will crash, if a non printable key was + // pressed, its char code (#0) is translated to upper-case and the the first + // character is accessed with Result[1]. + // We cannot catch this error in the WideString parameter variant as the string + // has length 0 already. + + // Force min. string length of 1 + if (ch = #0) then + Result := #0 + else + Result := WideStringUpperCase(WideString(ch)); +end; + +function WideStringUpperCase(const str: WideString): WideString; +begin + // On Linux and MacOSX the cwstring unit is necessary for Unicode function-calls. + // Otherwise you will get an EIntOverflow exception (thrown by unimplementedwidestring()). + // The Unicode manager cwstring does not work with MacOSX at the moment because + // of missing references to iconv. + // Note: Should be fixed now + + {.$IFNDEF DARWIN} + {.$IFDEF NOIGNORE} + Result := WideUpperCase(str) + {.$ELSE} + //Result := UTF8Decode(UpperCase(UTF8Encode(str))); + {.$ENDIF} +end; + +function WideStringLowerCase(ch: WideChar): WideString; +begin + // see WideStringUpperCase + if (ch = #0) then + Result := #0 + else + Result := WideStringLowerCase(WideString(ch)); +end; + +function WideStringLowerCase(const str: WideString): WideString; +begin + // see WideStringUpperCase + Result := WideLowerCase(str) +end; + +function WideStringReplaceChar(const text: WideString; search, rep: WideChar): WideString; +var + iPos : integer; +// sTemp : WideString; +begin +(* + result := text; + iPos := Pos(search, result); + while (iPos > 0) do + begin + sTemp := copy(result, iPos + length(search), length(result)); + result := copy(result, 1, iPos - 1) + rep + sTEmp; + iPos := Pos(search, result); + end; +*) + result := text; + + if search = rep then + exit; + + for iPos := 1 to length(result) do + begin + if result[iPos] = search then + result[iPos] := rep; + end; +end; + +initialization + InitUnicodeUtils; + +end. diff --git a/Lua/src/base/UXMLSong.pas b/Lua/src/base/UXMLSong.pas index 58b48789..e9751eba 100644 --- a/Lua/src/base/UXMLSong.pas +++ b/Lua/src/base/UXMLSong.pas @@ -34,7 +34,9 @@ interface {$I switches.inc} uses - Classes; + Classes, + UPath, + UUnicodeUtils; type TNote = record @@ -42,30 +44,30 @@ type Duration: Cardinal; Tone: Integer; NoteTyp: Byte; - Lyric: String; + Lyric: UTF8String; end; - ANote = Array of TNote; + ANote = array of TNote; TSentence = record Singer: Byte; Duration: Cardinal; Notes: ANote; end; - ASentence = Array of TSentence; + ASentence = array of TSentence; - TSongInfo = Record + TSongInfo = record ID: Cardinal; DualChannel: Boolean; - Header: Record - Artist: String; - Title: String; + Header: record + Artist: UTF8String; + Title: UTF8String; Gap: Cardinal; BPM: Real; Resolution: Byte; - Edition: String; - Genre: String; - Year: String; - Language: String; + Edition: UTF8String; + Genre: UTF8String; + Year: UTF8String; + Language: UTF8String; end; CountSentences: Cardinal; Sentences: ASentence; @@ -81,23 +83,23 @@ type 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; + function ParseLine(Line: RawByteString): Boolean; public SongInfo: TSongInfo; - ErrorMessage: String; - Edition: String; - SingstarVersion: String; + ErrorMessage: string; + Edition: UTF8String; + SingstarVersion: string; - Settings: Record + Settings: record DashReplacement: Char; end; - Constructor Create; + constructor Create; - Function ParseConfigforEdition(const Filename: String): String; + function ParseConfigForEdition(const Filename: IPath): String; - Function ParseSongHeader(const Filename: String): Boolean; //Parse Song Header only - Function ParseSong (const Filename: String): Boolean; //Parse whole Song + function ParseSongHeader(const Filename: IPath): Boolean; //Parse Song Header only + function ParseSong (const Filename: IPath): Boolean; //Parse whole Song end; const @@ -114,9 +116,12 @@ const DS_Both = 3; implementation -uses SysUtils, StrUtils; -Constructor TParser.Create; +uses + SysUtils, + StrUtils; + +constructor TParser.Create; begin inherited Create; ErrorMessage := ''; @@ -124,19 +129,24 @@ begin DecimalSeparator := '.'; end; -Function TParser.ParseSong (const Filename: String): Boolean; -var I: Integer; +function TParser.ParseSong(const Filename: IPath): Boolean; +var + I: Integer; + FileStream: TBinaryFileStream; begin Result := False; - if FileExists(Filename) then + if Filename.IsFile() then begin - SSFile := TStringList.Create; + ErrorMessage := 'Can''t open melody.xml file'; + SSFile := TStringList.Create; + FileStream := TBinaryFileStream.Create(Filename, fmOpenRead); try - ErrorMessage := 'Can''t open melody.xml file'; - SSFile.LoadFromFile(Filename); + SSFile.LoadFromStream(FileStream); + ErrorMessage := ''; Result := True; + I := 0; SongInfo.CountSentences := 0; @@ -153,7 +163,7 @@ begin SetLength(SongInfo.Sentences, 0); - While Result And (I < SSFile.Count) do + while Result and (I < SSFile.Count) do begin Result := ParseLine(SSFile.Strings[I]); @@ -162,21 +172,24 @@ begin finally SSFile.Free; + FileStream.Free; end; end; end; -Function TParser.ParseSongHeader (const Filename: String): Boolean; -var I: Integer; +function TParser.ParseSongHeader (const Filename: IPath): Boolean; +var + I: Integer; + Stream: TBinaryFileStream; begin Result := False; - if FileExists(Filename) then + + if Filename.IsFile() then begin SSFile := TStringList.Create; - SSFile.Clear; - + Stream := TBinaryFileStream.Create(Filename, fmOpenRead); try - SSFile.LoadFromFile(Filename); + SSFile.LoadFromStream(Stream); If (SSFile.Count > 0) then begin @@ -207,6 +220,7 @@ begin finally SSFile.Free; + Stream.Free; end; end else @@ -569,18 +583,20 @@ begin Result := true; end; -Function TParser.ParseConfigforEdition(const Filename: String): String; +Function TParser.ParseConfigForEdition(const Filename: IPath): String; var txt: TStringlist; + Stream: TBinaryFileStream; I: Integer; J, K: Integer; S: String; begin Result := ''; - txt := TStringlist.Create; - try - txt.LoadFromFile(Filename); + Stream := TBinaryFileStream.Create(Filename, fmOpenRead); + try + txt := TStringlist.Create; + txt.LoadFromStream(Stream); For I := 0 to txt.Count-1 do begin S := Trim(txt.Strings[I]); @@ -600,6 +616,7 @@ begin Edition := Result; finally txt.Free; + Stream.Free; end; end; diff --git a/Lua/src/encoding/Auto.inc b/Lua/src/encoding/Auto.inc new file mode 100644 index 00000000..2f7faa0c --- /dev/null +++ b/Lua/src/encoding/Auto.inc @@ -0,0 +1,136 @@ +{* 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$ + *} + +// Auto +// try to match the w3c regex and decode as unicode on match and as fallback if not match +// (copied from http://www.w3.org/International/questions/qa-forms-utf-8.en.php) +// +// m/\A( +// [\x09\x0A\x0D\x20-\x7E] # ASCII +// | [\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte +// | \xE0[\xA0-\xBF][\x80-\xBF] # excluding overlongs +// | [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte +// | \xED[\x80-\x9F][\x80-\xBF] # excluding surrogates +// | \xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3 +// | [\xF1-\xF3][\x80-\xBF]{3} # planes 4-15 +// | \xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16 +// )*\z/x + +type + TEncoderAuto = class(TEncoder) + public + function GetName(): AnsiString; override; + function Encode(const InStr: UCS4String; out OutStr: AnsiString): boolean; override; + function Decode(const InStr: AnsiString; out OutStr: UCS4String): boolean; override; + + constructor Create(const UTF8Encoder, FallbackEncoder: IEncoder); + + private + FallbackEncoder: IEncoder; + UTF8Encoder: IEncoder; + Regex: PPCRE; + RegexExtra: PPCREExtra; + end; + +function PCREGetMem(Size: SizeInt): Pointer; cdecl; +begin + GetMem(Result, Size); +end; + +procedure PCREFreeMem(P: Pointer); cdecl; +begin + FreeMem(P); +end; + +constructor TEncoderAuto.Create(const UTF8Encoder, FallbackEncoder: IEncoder); +var + Error: PChar; + ErrorOffset: Integer; +begin + // NOTICE: Log.LogError() is not possible here because it isn't loaded + inherited Create(); + self.FallbackEncoder := FallbackEncoder; + self.UTF8Encoder := UTF8Encoder; + + // Load and initialize PCRE Library + if LoadPCRE() then + begin + // compile regex + self.Regex := pcre_compile('\A([\x09\x0A\x0D\x20-\x7E]|[\xC2-\xDF][\x80-\xBF]|\xE0[\xA0-\xBF][\x80-\xBF]|[\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}|\xED[\x80-\x9F][\x80-\xBF]|\xF0[\x90-\xBF][\x80-\xBF]{2}|[\xF1-\xF3][\x80-\xBF]{3}|\xF4[\x80-\x8F][\x80-\xBF]{2})*\z', 0, @Error, @ErrorOffset, nil); + + if self.Regex = Nil then + begin + {$IFDEF CONSOLE} + writeln('ERROR: UTF8 Regex compilation failed: ', AnsiString(Error), ' at ', ErrorOffset); + {$ENDIF} + end + else + begin + // if compiled successfull, try to get more informations the speed up the matching + self.RegexExtra := pcre_study(self.Regex, 0, @Error); + + if Error <> Nil then + begin + {$IFDEF CONSOLE} + writeln('ERROR: UTF8 Regex study failed: ', AnsiString(Error)); + {$ENDIF} + end; + end; + end + else + begin + {$IFDEF CONSOLE} + writeln('ERROR: pcre not loaded. utf-8 autodetection will not work.'); + {$ENDIF} + end; +end; + +function TEncoderAuto.GetName(): AnsiString; +begin + Result := 'Auto'; +end; + +function TEncoderAuto.Decode(const InStr: AnsiString; out OutStr: UCS4String): boolean; +var + RegexResults: Integer; +begin + if (self.Regex <> Nil) then + begin + RegexResults := pcre_exec(Regex, RegexExtra, PChar(InStr), Length(InStr), 0, 0, Nil, 0); + + if RegexResults >= 0 then + begin + Result := UTF8Encoder.Decode(InStr, OutStr); + Exit; + end; + end; + + Result := FallbackEncoder.Decode(InStr, OutStr); +end; + +function TEncoderAuto.Encode(const InStr: UCS4String; out OutStr: AnsiString): boolean; +begin + Result := UTF8Encoder.Encode(InStr, OutStr); +end; diff --git a/Lua/src/encoding/CP1250.inc b/Lua/src/encoding/CP1250.inc new file mode 100644 index 00000000..5628156e --- /dev/null +++ b/Lua/src/encoding/CP1250.inc @@ -0,0 +1,236 @@ +{* 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$ + *} + +{* + * Windows-1250 Central/Eastern Europe + * (used by Ultrastar) + *} + +type + TEncoderCP1250 = class(TSingleByteEncoder) + public + function GetName(): AnsiString; override; + function DecodeChar(InChr: AnsiChar; out OutChr: UCS4Char): boolean; override; + function EncodeChar(InChr: UCS4Char; out OutChr: AnsiChar): boolean; override; + end; + +function TEncoderCP1250.GetName(): AnsiString; +begin + Result := 'CP1250'; +end; + +const + // Positions marked as #0 are invalid. + CP1250Table: array[128..255] of UCS4Char = ( + { $80 } + $20AC, 0, $201A, 0, $201E, $2026, $2020, $2021, + 0, $2030, $0160, $2039, $015A, $0164, $017D, $0179, + { $90 } + 0, $2018, $2019, $201C, $201D, $2022, $2013, $2014, + 0, $2122, $0161, $203A, $015B, $0165, $017E, $017A, + { $A0 } + $00A0, $02C7, $02D8, $0141, $00A4, $0104, $00A6, $00A7, + $00A8, $00A9, $015E, $00AB, $00AC, $00AD, $00AE, $017B, + { $B0 } + $00B0, $00B1, $02DB, $0142, $00B4, $00B5, $00B6, $00B7, + $00B8, $0105, $015F, $00BB, $013D, $02DD, $013E, $017C, + { $C0 } + $0154, $00C1, $00C2, $0102, $00C4, $0139, $0106, $00C7, + $010C, $00C9, $0118, $00CB, $011A, $00CD, $00CE, $010E, + { $D0 } + $0110, $0143, $0147, $00D3, $00D4, $0150, $00D6, $00D7, + $0158, $016E, $00DA, $0170, $00DC, $00DD, $0162, $00DF, + { $E0 } + $0155, $00E1, $00E2, $0103, $00E4, $013A, $0107, $00E7, + $010D, $00E9, $0119, $00EB, $011B, $00ED, $00EE, $010F, + { $F0 } + $0111, $0144, $0148, $00F3, $00F4, $0151, $00F6, $00F7, + $0159, $016F, $00FA, $0171, $00FC, $00FD, $0163, $02D9 + ); + +function TEncoderCP1250.DecodeChar(InChr: AnsiChar; out OutChr: UCS4Char): boolean; +begin + Result := true; + if (InChr < #128) then + OutChr := UCS4Char(Ord(InChr)) // use Ord() to avoid automatic conversion + else + begin + OutChr := CP1250Table[Ord(InChr)]; + if (OutChr = 0) then + begin + Result := false; + OutChr := Ord(ERROR_CHAR); + end; + end; +end; + +function TEncoderCP1250.EncodeChar(InChr: UCS4Char; out OutChr: AnsiChar): boolean; +begin + if (InChr < 128) then + begin + OutChr := AnsiChar(Ord(InChr)); + Result := true; + end + else + begin + case InChr of + $20AC: OutChr := #128; + // invalid: #129 + $201A: OutChr := #130; + // invalid: #131 + $201E: OutChr := #132; + $2026: OutChr := #133; + $2020: OutChr := #134; + $2021: OutChr := #135; + // invalid: #136 + $2030: OutChr := #137; + $0160: OutChr := #138; + $2039: OutChr := #139; + $015A: OutChr := #140; + $0164: OutChr := #141; + $017D: OutChr := #142; + $0179: OutChr := #143; + // invalid: #144 + $2018: OutChr := #145; + $2019: OutChr := #146; + $201C: OutChr := #147; + $201D: OutChr := #148; + $2022: OutChr := #149; + $2013: OutChr := #150; + $2014: OutChr := #151; + // invalid: #152 + $2122: OutChr := #153; + $0161: OutChr := #154; + $203A: OutChr := #155; + $015B: OutChr := #156; + $0165: OutChr := #157; + $017E: OutChr := #158; + $017A: OutChr := #159; + $00A0: OutChr := #160; + $02C7: OutChr := #161; + $02D8: OutChr := #162; + $0141: OutChr := #163; + $00A4: OutChr := #164; + $0104: OutChr := #165; + $00A6: OutChr := #166; + $00A7: OutChr := #167; + $00A8: OutChr := #168; + $00A9: OutChr := #169; + $015E: OutChr := #170; + $00AB: OutChr := #171; + $00AC: OutChr := #172; + $00AD: OutChr := #173; + $00AE: OutChr := #174; + $017B: OutChr := #175; + $00B0: OutChr := #176; + $00B1: OutChr := #177; + $02DB: OutChr := #178; + $0142: OutChr := #179; + $00B4: OutChr := #180; + $00B5: OutChr := #181; + $00B6: OutChr := #182; + $00B7: OutChr := #183; + $00B8: OutChr := #184; + $0105: OutChr := #185; + $015F: OutChr := #186; + $00BB: OutChr := #187; + $013D: OutChr := #188; + $02DD: OutChr := #189; + $013E: OutChr := #190; + $017C: OutChr := #191; + $0154: OutChr := #192; + $00C1: OutChr := #193; + $00C2: OutChr := #194; + $0102: OutChr := #195; + $00C4: OutChr := #196; + $0139: OutChr := #197; + $0106: OutChr := #198; + $00C7: OutChr := #199; + $010C: OutChr := #200; + $00C9: OutChr := #201; + $0118: OutChr := #202; + $00CB: OutChr := #203; + $011A: OutChr := #204; + $00CD: OutChr := #205; + $00CE: OutChr := #206; + $010E: OutChr := #207; + $0110: OutChr := #208; + $0143: OutChr := #209; + $0147: OutChr := #210; + $00D3: OutChr := #211; + $00D4: OutChr := #212; + $0150: OutChr := #213; + $00D6: OutChr := #214; + $00D7: OutChr := #215; + $0158: OutChr := #216; + $016E: OutChr := #217; + $00DA: OutChr := #218; + $0170: OutChr := #219; + $00DC: OutChr := #220; + $00DD: OutChr := #221; + $0162: OutChr := #222; + $00DF: OutChr := #223; + $0155: OutChr := #224; + $00E1: OutChr := #225; + $00E2: OutChr := #226; + $0103: OutChr := #227; + $00E4: OutChr := #228; + $013A: OutChr := #229; + $0107: OutChr := #230; + $00E7: OutChr := #231; + $010D: OutChr := #232; + $00E9: OutChr := #233; + $0119: OutChr := #234; + $00EB: OutChr := #235; + $011B: OutChr := #236; + $00ED: OutChr := #237; + $00EE: OutChr := #238; + $010F: OutChr := #239; + $0111: OutChr := #240; + $0144: OutChr := #241; + $0148: OutChr := #242; + $00F3: OutChr := #243; + $00F4: OutChr := #244; + $0151: OutChr := #245; + $00F6: OutChr := #246; + $00F7: OutChr := #247; + $0159: OutChr := #248; + $016F: OutChr := #249; + $00FA: OutChr := #250; + $0171: OutChr := #251; + $00FC: OutChr := #252; + $00FD: OutChr := #253; + $0163: OutChr := #254; + $02D9: OutChr := #255; + else begin + OutChr := ERROR_CHAR; + Result := false; + Exit; + end; + end; + Result := true; + end; +end; + diff --git a/Lua/src/encoding/CP1252.inc b/Lua/src/encoding/CP1252.inc new file mode 100644 index 00000000..f7d3f8ea --- /dev/null +++ b/Lua/src/encoding/CP1252.inc @@ -0,0 +1,122 @@ +{* 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$ + *} + +{* + * Windows-1252 Western Europe + * (used by UltraStar Deluxe < 1.1) + *} + +type + TEncoderCP1252 = class(TSingleByteEncoder) + public + function GetName(): AnsiString; override; + function DecodeChar(InChr: AnsiChar; out OutChr: UCS4Char): boolean; override; + function EncodeChar(InChr: UCS4Char; out OutChr: AnsiChar): boolean; override; + end; + +function TEncoderCP1252.GetName(): AnsiString; +begin + Result := 'CP1252'; +end; + +const + // Positions marked as #0 are invalid. + CP1252Table: array[128..159] of UCS4Char = ( + { $80 } + $20AC, 0, $201A, $0192, $201E, $2026, $2020, $2021, + $02C6, $2030, $0160, $2039, $0152, 0, $017D, 0, + { $90 } + 0, $2018, $2019, $201C, $201D, $2022, $2013, $2014, + $02DC, $2122, $0161, $203A, $0153, 0, $017E, $0178 + ); + +function TEncoderCP1252.DecodeChar(InChr: AnsiChar; out OutChr: UCS4Char): boolean; +begin + Result := true; + if (InChr < #128) or (InChr >= #160) then + OutChr := UCS4Char(Ord(InChr)) // use Ord() to avoid automatic conversion + else + begin + OutChr := CP1252Table[Ord(InChr)]; + if (OutChr = 0) then + begin + Result := false; + OutChr := Ord(ERROR_CHAR); + end; + end; +end; + +function TEncoderCP1252.EncodeChar(InChr: UCS4Char; out OutChr: AnsiChar): boolean; +begin + if (InChr < 128) or ((InChr >= 160) and (InChr <= 255)) then + begin + OutChr := AnsiChar(Ord(InChr)); + Result := true; + end + else + begin + case InChr of + $20AC: OutChr := #128; + // invalid: #129 + $201A: OutChr := #130; + $0192: OutChr := #131; + $201E: OutChr := #132; + $2026: OutChr := #133; + $2020: OutChr := #134; + $2021: OutChr := #135; + $02C6: OutChr := #136; + $2030: OutChr := #137; + $0160: OutChr := #138; + $2039: OutChr := #139; + $0152: OutChr := #140; + // invalid: #141 + $017D: OutChr := #142; + // invalid: #143 + // invalid: #144 + $2018: OutChr := #145; + $2019: OutChr := #146; + $201C: OutChr := #147; + $201D: OutChr := #148; + $2022: OutChr := #149; + $2013: OutChr := #150; + $2014: OutChr := #151; + $02DC: OutChr := #152; + $2122: OutChr := #153; + $0161: OutChr := #154; + $203A: OutChr := #155; + $0153: OutChr := #156; + // invalid: #157 + $017E: OutChr := #158; + $0178: OutChr := #159; + else begin + OutChr := ERROR_CHAR; + Result := false; + Exit; + end; + end; + Result := true; + end; +end; + diff --git a/Lua/src/base/UModules.pas b/Lua/src/encoding/Locale.inc index 97494180..a3cdcebc 100644 --- a/Lua/src/base/UModules.pas +++ b/Lua/src/encoding/Locale.inc @@ -23,33 +23,33 @@ * $Id$ *} -unit UModules; - -interface - -{$IFDEF FPC} - {$MODE Delphi} -{$ENDIF} - -{$I switches.inc} - -{********************* - UModules - Unit Contains all used Modules in its uses clausel - and a const with an array of all Modules to load -*********************} - -uses - UCoreModule, - UPluginLoader; - -const - CORE_MODULES_TO_LOAD: Array[0..2] of cCoreModule = ( - TPluginLoader, //First because it has to look if there are Module replacements (Feature o/t Future) - TCoreModule, //Remove this later, just a dummy - TtehPlugins //Represents the Plugins. Last because they may use CoreModules Services etc. - ); +{* + * Locale + *} -implementation +type + TEncoderLocale = class(TEncoder) + public + function GetName(): AnsiString; override; + function Encode(const InStr: UCS4String; out OutStr: AnsiString): boolean; override; + function Decode(const InStr: AnsiString; out OutStr: UCS4String): boolean; override; + end; + +function TEncoderLocale.GetName(): AnsiString; +begin + Result := 'LOCALE'; +end; + +function TEncoderLocale.Decode(const InStr: AnsiString; out OutStr: UCS4String): boolean; +begin + OutStr := WideStringToUCS4String(InStr); // use implicit conversion + Result := true; +end; + +function TEncoderLocale.Encode(const InStr: UCS4String; out OutStr: AnsiString): boolean; +begin + OutStr := UCS4StringToWideString(InStr); // use implicit conversion + // any way to check for errors? + Result := true; +end; -end.
\ No newline at end of file diff --git a/Lua/src/encoding/UTF8.inc b/Lua/src/encoding/UTF8.inc new file mode 100644 index 00000000..43eacfbd --- /dev/null +++ b/Lua/src/encoding/UTF8.inc @@ -0,0 +1,70 @@ +{* 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$ + *} + +{* + * UTF-8 + *} + +type + TEncoderUTF8 = class(TEncoder) + public + function GetName(): AnsiString; override; + function Encode(const InStr: UCS4String; out OutStr: AnsiString): boolean; override; + function Decode(const InStr: AnsiString; out OutStr: UCS4String): boolean; override; + end; + +function TEncoderUTF8.GetName(): AnsiString; +begin + Result := 'UTF8'; +end; + +function TEncoderUTF8.Decode(const InStr: AnsiString; out OutStr: UCS4String): boolean; +var + I: integer; + StrPtr: PAnsiChar; +begin + // UTF8Decode() may crash with FPC < 2.2.2 if the input string is not UTF-8 + // encoded. Newer versions do not crash but do not signal errors either. + // So let's implement this stuff again. + Result := true; + SetLength(OutStr, Length(InStr)+1); + I := 0; + StrPtr := PChar(InStr); + while (StrPtr^ <> #0) do + begin + if (not NextCharUTF8(StrPtr, OutStr[I])) then + Result := false;; + Inc(I); + end; + SetLength(OutStr, I+1); + OutStr[High(OutStr)] := 0; +end; + +function TEncoderUTF8.Encode(const InStr: UCS4String; out OutStr: AnsiString): boolean; +begin + OutStr := UCS4ToUTF8String(InStr); + Result := true; +end; + diff --git a/Lua/src/lib/FreeImage/FreeBitmap.pas b/Lua/src/lib/FreeImage/FreeBitmap.pas index 4e5f50a4..d32fb5cb 100644 --- a/Lua/src/lib/FreeImage/FreeBitmap.pas +++ b/Lua/src/lib/FreeImage/FreeBitmap.pas @@ -33,7 +33,7 @@ unit FreeBitmap; {$IFDEF FPC} {$MODE Delphi} - {$H+} // use AnsiString + {$H+} // use long strings {$ENDIF} interface diff --git a/Lua/src/lib/JEDI-SDL/OpenGL/Pas/glext.pas b/Lua/src/lib/JEDI-SDL/OpenGL/Pas/glext.pas index 1fc70f8a..871247a9 100644 --- a/Lua/src/lib/JEDI-SDL/OpenGL/Pas/glext.pas +++ b/Lua/src/lib/JEDI-SDL/OpenGL/Pas/glext.pas @@ -4282,32 +4282,33 @@ begin if (Pos(' ', extension) <> 0) or (extension = '') then begin - Result := FALSE; + Result := false; Exit; end; if searchIn = '' then extensions := glGetString(GL_EXTENSIONS) else - //StrLCopy( extensions, searchIn, StrLen(searchIn)+1 ); + //StrLCopy(extensions, searchIn, StrLen(searchIn) + 1); extensions := searchIn; start := extensions; - while TRUE do + while true do begin - where := StrPos(start, extension ); - if where = nil then Break; - terminator := Pointer(Integer(where) + Integer( strlen( extension ) ) ); - if (where = start) or (PChar(Integer(where) - 1)^ = ' ') then + where := StrPos(start, extension); + if where = nil then + Break; + terminator := where + Length(extension); + if (where = start) or ((where - 1)^ = ' ') then begin if (terminator^ = ' ') or (terminator^ = #0) then begin - Result := TRUE; + Result := true; Exit; end; end; start := terminator; end; - Result := FALSE; + Result := false; end; diff --git a/Lua/src/lib/SQLite/SQLite3.pas b/Lua/src/lib/SQLite/SQLite3.pas index 9537606c..7b7207c4 100644 --- a/Lua/src/lib/SQLite/SQLite3.pas +++ b/Lua/src/lib/SQLite/SQLite3.pas @@ -10,7 +10,7 @@ unit SQLite3; {$IFDEF FPC} {$MODE DELPHI} - {$H+} (* use AnsiString *) + {$H+} (* use long strings *) {$PACKENUM 4} (* use 4-byte enums *) {$PACKRECORDS C} (* C/C++-compatible record packing *) {$ELSE} diff --git a/Lua/src/lib/SQLite/SQLiteTable3.pas b/Lua/src/lib/SQLite/SQLiteTable3.pas index 7df76363..3aed54a4 100644 --- a/Lua/src/lib/SQLite/SQLiteTable3.pas +++ b/Lua/src/lib/SQLite/SQLiteTable3.pas @@ -139,6 +139,7 @@ type procedure Commit; procedure Rollback; function TableExists(TableName: string): boolean; + function ContainsColumn(Table: String; Column: String) : boolean; function GetLastInsertRowID: int64; function GetLastChangedRows: int64; procedure SetTimeout(Value: integer); @@ -759,6 +760,26 @@ begin end; end; +function TSQLiteDatabase.ContainsColumn(Table: String; Column: String) : boolean; +var + sql: string; + ds: TSqliteTable; + i : integer; +begin + sql := 'PRAGMA TABLE_INFO('+Table+');'; + ds := self.GetTable(sql); + try + Result := false; + while (ds.Next() and not Result and not ds.EOF) do + begin + if ds.FieldAsString(1) = Column then + Result := true; + end; + finally + ds.Free; + end; +end; + procedure TSQLiteDatabase.SetTimeout(Value: integer); begin SQLite3_BusyTimeout(self.fDB, Value); diff --git a/Lua/src/lib/TntUnicodeControls/License.txt b/Lua/src/lib/TntUnicodeControls/License.txt new file mode 100644 index 00000000..8ac7f75b --- /dev/null +++ b/Lua/src/lib/TntUnicodeControls/License.txt @@ -0,0 +1,11 @@ +TntWare Delphi Unicode Controls + http://www.tntware.com/delphicontrols/unicode/ + +Copyright (c) 2002-2007, Troy Wolbrink (www.tntware.com) + +License +Redistribution and use in binary forms, with or without modification, are permitted. Redistribution and use in source forms, with or without modification, are permitted provided that the redistributions of source code retain the above copyright. + +Disclaimer +This software is provided by the author "as is" and any express or implied warranties, including, but not limited to, the implied warranties of merchantability and fitness for a particular purpose are disclaimed. In no event shall the author be liable for any direct, indirect, incidental, special, exemplary, or consequential damages (including, but not limited to, procurement of substitute goods or services; loss of use, data, or profits; or business interruption) however caused and on any theory of liability, whether in contract, strict liability, or tort (including negligence or otherwise) arising in any way out of the use of this software, even if advised of the possibility of such damage. + diff --git a/Lua/src/lib/TntUnicodeControls/Readme.txt b/Lua/src/lib/TntUnicodeControls/Readme.txt new file mode 100644 index 00000000..a2d8f799 --- /dev/null +++ b/Lua/src/lib/TntUnicodeControls/Readme.txt @@ -0,0 +1,53 @@ + ** Tnt Delphi UNICODE Controls Project ** + +Website: http://tnt.ccci.org/delphi_unicode_controls/ +Email: troy.wolbrink@ccci.org + +These controls are provided as-is, with no implied warranty. They are freely available for you to use in your own projects. Please let me know if you have found them helpful. Also, please let me know if you find any bugs or other areas of needed improvement. + + +---Delphi Installation-------------------------- + +The most simple way to install these components is by opening the appropriate design package in Delphi and clicking on the big "Install" button. For instance, Delphi 5's design package is TntUnicodeVcl_D50.dpk. + +For BCB 2006 and newer, open the appropriate design package in the packages\bcbx\ folder using the Delphi personality. After compiling and installing, you should be able to use the components in both the Delphi and BCB personality. Remember to set the library path in menu "Tools->Options" for both the C++ Builder and the Delphi. + + +---A note on fonts---------------------- + +The default TFont uses "MS Sans Serif" which doesn't work well with most non-ANSI characters. I'd recommend using a TrueType font such as "Tahoma" if it is installed on the machine. To make TFont use a different font like "Tahoma" add this to the first line in the project: + + Graphics.DefFontData.Name := 'Tahoma'; + +You might have to include "Graphics" in the file's uses clauses. Furthermore, adding this line of code to the project will cause the changed setting to only be applied at runtime, not at design time. To make a designtime change, you'd have to add this line to the initialization section of a unit in a design package. + +Regarding the IDE, I use GExperts to change the font of the Object Inspector. The Wide String List editor uses the font used by the object inspector. + +Also keep in mind that the font used by certain message boxes come from that set by Windows' Display properties. + + +---Background---------------------------- + +Designing software for an international audience, I've always wanted to write a full UNICODE application. My approach so far, has been to write Unicode on the inside, and MBCS on the outside. This has always been frustrating, because (even on Windows NT/2000/XP which provide native Unicode window controls) the WideStrings inside my application and databases were always confined to an ANSI VCL. And, since the VCL was designed to wrap the low-level Windows details, why shouldn't the VCL hide the fact that sometimes native Unicode controls are not possible on the given version of Windows. I believe the VCL should be written with a Unicode interface, even if it must (at times) deal with an ANSI operating system. For example, TEdit should expose Text as a WideString, even if it has to convert the WideString to an AnsiString on the Windows 9X platform. + +In the past, the ANSI VCL may have made a little sense, considering that there were many more users of Windows 9X, than Windows NT. There would have been some performance penalty to use WideStrings on the Windows 9X platform. But with the faster computers of today, and with more people using platforms such as Windows 2000 and Windows XP, the ANSI VCL just doesn't make sense anymore. In fact, having to use the the ANSI VCL on Windows NT/2000/XP is slower because of the constant conversion to and from UNICODE inside Windows. + +My coding signature is Tnt. I will use this to denote my classes from others. + +For more information about me: <http://home.ccci.org/wolbrink/> +Some of my software projects (all written in Delphi). + TntMPD (contact manager for missionaries) + <http://www.tntmpd.com/> + Jesus Film Screen Saver + <http://home.ccci.org/wolbrink/screensaver.htm> + ActiveX SCR control + <http://tnt.ccci.org/download/activex_scr/ActiveXSCR.exe> + +---Design Goals---------------------------- + +I want the controls to work on Windows 95, 98, ME, NT, 2000, XP, etc. I want a single EXE for all platforms. Of course, full UNICODE support is only truly available on NT/2000/XP. In other words, the controls should automatically scale to take advantage of native Unicode support when possible. + +I want the controls to inherit from the Delphi VCL. I want to reuse as much code as possible. For the most part this makes sense. The only sticky part is where text messages get passed around. But I believe I've gotten past this through strategic subclassing at various points in the message flow chain. To give a rough comparison of why this is so important, check out the following chart which compares the lines of code in the VCL for a given control (4,397 in all), and the lines of code required in my descendent controls (655 in all). Besides saving lines of code, I get the advantage of automatically inheriting new features as new versions of Delphi come out. One such example is the AlphaBlending feature in the Delphi 6 TForm. Even though I use Delphi 5 now, I won't have to add any code to get this new feature. + +---More Interesting Information---------------------------- +Case Study: Porting an MFC Application to Unicode: It looks like the FrontPage 2002 team did the roughly the same thing to MFC as what I'm doing to the VCL. They did this with the same goal in mind: to support Unicode as much as possible depending on the support offered by Windows. Another goal was "Don’t abandon MFC; don’t rewrite app". Because they still want to support Windows 9X using the same worldwide EXE used everywhere. They couldn't just compile with the _UNICODE directive. They had to start with the ANSI MFC, strategically subclassing window procedures at just the right places. Hmmm... sounds familiar.
\ No newline at end of file diff --git a/Lua/src/lib/TntUnicodeControls/TntClasses.pas b/Lua/src/lib/TntUnicodeControls/TntClasses.pas new file mode 100644 index 00000000..be043421 --- /dev/null +++ b/Lua/src/lib/TntUnicodeControls/TntClasses.pas @@ -0,0 +1,1799 @@ + +{*****************************************************************************} +{ } +{ Tnt Delphi Unicode Controls } +{ http://www.tntware.com/delphicontrols/unicode/ } +{ Version: 2.3.0 } +{ } +{ Copyright (c) 2002-2007, Troy Wolbrink (troy.wolbrink@tntware.com) } +{ } +{*****************************************************************************} + +unit TntClasses; + +{$IFDEF FPC} + {$MODE Delphi} +{$ENDIF} + +{$INCLUDE TntCompilers.inc} + +interface + +{ TODO: Consider: TTntRegIniFile, TTntMemIniFile (consider if UTF8 fits into this solution). } + +{***********************************************} +{ WideChar-streaming implemented by Maël Hörz } +{***********************************************} + +uses + Classes, SysUtils, Windows, + {$IFNDEF COMPILER_10_UP} + TntWideStrings, + {$ELSE} + WideStrings, + {$ENDIF} + ActiveX, Contnrs; + +// ......... introduced ......... +type + TTntStreamCharSet = (csAnsi, csUnicode, csUnicodeSwapped, csUtf8); + +function AutoDetectCharacterSet(Stream: TStream): TTntStreamCharSet; + +//--------------------------------------------------------------------------------------------- +// Tnt - Classes +//--------------------------------------------------------------------------------------------- + +{TNT-WARN ExtractStrings} +{TNT-WARN LineStart} +{TNT-WARN TStringStream} // TODO: Implement a TWideStringStream + +// A potential implementation of TWideStringStream can be found at: +// http://kdsxml.cvs.sourceforge.net/kdsxml/Global/KDSClasses.pas?revision=1.10&view=markup + +procedure TntPersistent_AfterInherited_DefineProperties(Filer: TFiler; Instance: TPersistent); + +type +{TNT-WARN TFileStream} + TTntFileStream = class(THandleStream) + public + constructor Create(const FileName: WideString; Mode: Word); + destructor Destroy; override; + end; + +{TNT-WARN TMemoryStream} + TTntMemoryStream = class(TMemoryStream{TNT-ALLOW TMemoryStream}) + public + procedure LoadFromFile(const FileName: WideString); + procedure SaveToFile(const FileName: WideString); + end; + +{TNT-WARN TResourceStream} + TTntResourceStream = class(TCustomMemoryStream) + private + HResInfo: HRSRC; + HGlobal: THandle; + procedure Initialize(Instance: THandle; Name, ResType: PWideChar); + public + constructor Create(Instance: THandle; const ResName: WideString; ResType: PWideChar); + constructor CreateFromID(Instance: THandle; ResID: Word; ResType: PWideChar); + destructor Destroy; override; + function Write(const Buffer; Count: Longint): Longint; override; + procedure SaveToFile(const FileName: WideString); + end; + + TTntStrings = class; + +{TNT-WARN TAnsiStrings} + TAnsiStrings{TNT-ALLOW TAnsiStrings} = class(TStrings{TNT-ALLOW TStrings}) + public + procedure LoadFromFile(const FileName: WideString); reintroduce; + procedure SaveToFile(const FileName: WideString); reintroduce; + procedure LoadFromFileEx(const FileName: WideString; CodePage: Cardinal); + procedure SaveToFileEx(const FileName: WideString; CodePage: Cardinal); + procedure LoadFromStreamEx(Stream: TStream; CodePage: Cardinal); virtual; abstract; + procedure SaveToStreamEx(Stream: TStream; CodePage: Cardinal); virtual; abstract; + end; + + TAnsiStringsForWideStringsAdapter = class(TAnsiStrings{TNT-ALLOW TAnsiStrings}) + private + FWideStrings: TTntStrings; + FAdapterCodePage: Cardinal; + protected + function Get(Index: Integer): AnsiString; override; + procedure Put(Index: Integer; const S: AnsiString); override; + function GetCount: Integer; override; + function GetObject(Index: Integer): TObject; override; + procedure PutObject(Index: Integer; AObject: TObject); override; + procedure SetUpdateState(Updating: Boolean); override; + function AdapterCodePage: Cardinal; dynamic; + public + constructor Create(AWideStrings: TTntStrings; _AdapterCodePage: Cardinal = 0); + procedure Clear; override; + procedure Delete(Index: Integer); override; + procedure Insert(Index: Integer; const S: AnsiString); override; + procedure LoadFromStreamEx(Stream: TStream; CodePage: Cardinal); override; + procedure SaveToStreamEx(Stream: TStream; CodePage: Cardinal); override; + end; + +{TNT-WARN TStrings} + TTntStrings = class(TWideStrings) + private + FLastFileCharSet: TTntStreamCharSet; + FAnsiStrings: TAnsiStrings{TNT-ALLOW TAnsiStrings}; + procedure SetAnsiStrings(const Value: TAnsiStrings{TNT-ALLOW TAnsiStrings}); + procedure ReadData(Reader: TReader); + procedure ReadDataUTF7(Reader: TReader); + procedure ReadDataUTF8(Reader: TReader); + procedure WriteDataUTF7(Writer: TWriter); + protected + procedure DefineProperties(Filer: TFiler); override; + public + constructor Create; + destructor Destroy; override; + + procedure LoadFromFile(const FileName: WideString); override; + procedure LoadFromStream(Stream: TStream); override; + procedure LoadFromStream_BOM(Stream: TStream; WithBOM: Boolean); virtual; + + procedure SaveToFile(const FileName: WideString); override; + procedure SaveToStream(Stream: TStream); override; + procedure SaveToStream_BOM(Stream: TStream; WithBOM: Boolean); virtual; + + property LastFileCharSet: TTntStreamCharSet read FLastFileCharSet; + published + property AnsiStrings: TAnsiStrings{TNT-ALLOW TAnsiStrings} read FAnsiStrings write SetAnsiStrings stored False; + end; + +{ TTntStringList class } + + TTntStringList = class; + TWideStringListSortCompare = function(List: TTntStringList; Index1, Index2: Integer): Integer; + +{TNT-WARN TStringList} + TTntStringList = class(TTntStrings) + private + FUpdating: Boolean; + FList: PWideStringItemList; + FCount: Integer; + FCapacity: Integer; + FSorted: Boolean; + FDuplicates: TDuplicates; + FCaseSensitive: Boolean; + FOnChange: TNotifyEvent; + FOnChanging: TNotifyEvent; + procedure ExchangeItems(Index1, Index2: Integer); + procedure Grow; + procedure QuickSort(L, R: Integer; SCompare: TWideStringListSortCompare); + procedure SetSorted(Value: Boolean); + procedure SetCaseSensitive(const Value: Boolean); + protected + procedure Changed; virtual; + procedure Changing; virtual; + function Get(Index: Integer): WideString; override; + function GetCapacity: Integer; override; + function GetCount: Integer; override; + function GetObject(Index: Integer): TObject; override; + procedure Put(Index: Integer; const S: WideString); override; + procedure PutObject(Index: Integer; AObject: TObject); override; + procedure SetCapacity(NewCapacity: Integer); override; + procedure SetUpdateState(Updating: Boolean); override; + function CompareStrings(const S1, S2: WideString): Integer; override; + procedure InsertItem(Index: Integer; const S: WideString; AObject: TObject); virtual; + public + destructor Destroy; override; + function Add(const S: WideString): Integer; override; + function AddObject(const S: WideString; AObject: TObject): Integer; override; + procedure Clear; override; + procedure Delete(Index: Integer); override; + procedure Exchange(Index1, Index2: Integer); override; + function Find(const S: WideString; var Index: Integer): Boolean; virtual; + function IndexOf(const S: WideString): Integer; override; + function IndexOfName(const Name: WideString): Integer; override; + procedure Insert(Index: Integer; const S: WideString); override; + procedure InsertObject(Index: Integer; const S: WideString; + AObject: TObject); override; + procedure Sort; virtual; + procedure CustomSort(Compare: TWideStringListSortCompare); virtual; + property Duplicates: TDuplicates read FDuplicates write FDuplicates; + property Sorted: Boolean read FSorted write SetSorted; + property CaseSensitive: Boolean read FCaseSensitive write SetCaseSensitive; + property OnChange: TNotifyEvent read FOnChange write FOnChange; + property OnChanging: TNotifyEvent read FOnChanging write FOnChanging; + end; + +// ......... introduced ......... +type + TListTargetCompare = function (Item, Target: Pointer): Integer; + +function FindSortedListByTarget(List: TList; TargetCompare: TListTargetCompare; + Target: Pointer; var Index: Integer): Boolean; + +function ClassIsRegistered(const clsid: TCLSID): Boolean; + +var + RuntimeUTFStreaming: Boolean; + +type + TBufferedAnsiString = class(TObject) + private + FStringBuffer: AnsiString; + LastWriteIndex: Integer; + public + procedure Clear; + procedure AddChar(const wc: AnsiChar); + procedure AddString(const s: AnsiString); + procedure AddBuffer(Buff: PAnsiChar; Chars: Integer); + function Value: AnsiString; + function BuffPtr: PAnsiChar; + end; + + TBufferedWideString = class(TObject) + private + FStringBuffer: WideString; + LastWriteIndex: Integer; + public + procedure Clear; + procedure AddChar(const wc: WideChar); + procedure AddString(const s: WideString); + procedure AddBuffer(Buff: PWideChar; Chars: Integer); + function Value: WideString; + function BuffPtr: PWideChar; + end; + + TBufferedStreamReader = class(TStream) + private + FStream: TStream; + FStreamSize: Integer; + FBuffer: array of Byte; + FBufferSize: Integer; + FBufferStartPosition: Integer; + FVirtualPosition: Integer; + procedure UpdateBufferFromPosition(StartPos: Integer); + public + constructor Create(Stream: TStream; BufferSize: Integer = 1024); + function Read(var Buffer; Count: Longint): Longint; override; + function Write(const Buffer; Count: Longint): Longint; override; + function Seek(Offset: Longint; Origin: Word): Longint; override; + end; + +// "synced" wide string +type TSetAnsiStrEvent = procedure(const Value: AnsiString) of object; +function GetSyncedWideString(var WideStr: WideString; const AnsiStr: AnsiString): WideString; +procedure SetSyncedWideString(const Value: WideString; var WideStr: WideString; + const AnsiStr: AnsiString; SetAnsiStr: TSetAnsiStrEvent); + +type + TWideComponentHelper = class(TComponent) + private + FComponent: TComponent; + protected + procedure Notification(AComponent: TComponent; Operation: TOperation); override; + public + constructor Create(AOwner: TComponent); override; + constructor CreateHelper(AOwner: TComponent; ComponentHelperList: TComponentList); + end; + +function FindWideComponentHelper(ComponentHelperList: TComponentList; Component: TComponent): TWideComponentHelper; + +implementation + +uses + RTLConsts, ComObj, Math, + Registry, TypInfo, TntSystem, TntSysUtils; + +{ TntPersistent } + +//=========================================================================== +// The Delphi 5 Classes.pas never supported the streaming of WideStrings. +// The Delphi 6 Classes.pas supports WideString streaming. But it's too bad that +// the Delphi 6 IDE doesn't use the updated Classes.pas. Switching between Form/Text +// mode corrupts extended characters in WideStrings even under Delphi 6. +// Delphi 7 seems to finally get right. But let's keep the UTF7 support at design time +// to enable sharing source code with previous versions of Delphi. +// +// The purpose of this solution is to store WideString properties which contain +// non-ASCII chars in the form of UTF7 under the old property name + '_UTF7'. +// +// Special thanks go to Francisco Leong for helping to develop this solution. +// + +{ TTntWideStringPropertyFiler } +type + TTntWideStringPropertyFiler = class + private + FInstance: TPersistent; + FPropInfo: PPropInfo; + procedure ReadDataUTF8(Reader: TReader); + procedure ReadDataUTF7(Reader: TReader); + procedure WriteDataUTF7(Writer: TWriter); + public + procedure DefineProperties(Filer: TFiler; Instance: TPersistent; PropName: AnsiString); + end; + +function ReaderNeedsUtfHelp(Reader: TReader): Boolean; +begin + if Reader.Owner = nil then + Result := False { designtime - visual form inheritance ancestor } + else if csDesigning in Reader.Owner.ComponentState then + {$IFDEF COMPILER_7_UP} + Result := False { Delphi 7+: designtime - doesn't need UTF help. } + {$ELSE} + Result := True { Delphi 6: designtime - always needs UTF help. } + {$ENDIF} + else + Result := RuntimeUTFStreaming; { runtime } +end; + +procedure TTntWideStringPropertyFiler.ReadDataUTF8(Reader: TReader); +begin + if ReaderNeedsUtfHelp(Reader) then + SetWideStrProp(FInstance, FPropInfo, UTF8ToWideString(Reader.ReadString)) + else + Reader.ReadString; { do nothing with Result } +end; + +procedure TTntWideStringPropertyFiler.ReadDataUTF7(Reader: TReader); +begin + if ReaderNeedsUtfHelp(Reader) then + SetWideStrProp(FInstance, FPropInfo, UTF7ToWideString(Reader.ReadString)) + else + Reader.ReadString; { do nothing with Result } +end; + +procedure TTntWideStringPropertyFiler.WriteDataUTF7(Writer: TWriter); +begin + Writer.WriteString(WideStringToUTF7(GetWideStrProp(FInstance, FPropInfo))); +end; + +procedure TTntWideStringPropertyFiler.DefineProperties(Filer: TFiler; Instance: TPersistent; + PropName: AnsiString); + + {$IFNDEF COMPILER_7_UP} + function HasData: Boolean; + var + CurrPropValue: WideString; + begin + // must be stored + Result := IsStoredProp(Instance, FPropInfo); + if Result + and (Filer.Ancestor <> nil) + and (GetPropInfo(Filer.Ancestor, PropName, [tkWString]) <> nil) then + begin + // must be different than ancestor + CurrPropValue := GetWideStrProp(Instance, FPropInfo); + Result := CurrPropValue <> GetWideStrProp(Filer.Ancestor, GetPropInfo(Filer.Ancestor, PropName)); + end; + if Result then begin + // must be non-blank and different than UTF8 (implies all ASCII <= 127) + CurrPropValue := GetWideStrProp(Instance, FPropInfo); + Result := (CurrPropValue <> '') and (WideStringToUTF8(CurrPropValue) <> CurrPropValue); + end; + end; + {$ENDIF} + +begin + FInstance := Instance; + FPropInfo := GetPropInfo(Instance, PropName, [tkWString]); + if FPropInfo <> nil then begin + // must be published (and of type WideString) + Filer.DefineProperty(PropName + 'W', ReadDataUTF8, nil, False); + {$IFDEF COMPILER_7_UP} + Filer.DefineProperty(PropName + '_UTF7', ReadDataUTF7, WriteDataUTF7, False); + {$ELSE} + Filer.DefineProperty(PropName + '_UTF7', ReadDataUTF7, WriteDataUTF7, HasData); + {$ENDIF} + end; + FInstance := nil; + FPropInfo := nil; +end; + +{ TTntWideCharPropertyFiler } +type + TTntWideCharPropertyFiler = class + private + FInstance: TPersistent; + FPropInfo: PPropInfo; + {$IFNDEF COMPILER_9_UP} + FWriter: TWriter; + procedure GetLookupInfo(var Ancestor: TPersistent; + var Root, LookupRoot, RootAncestor: TComponent); + {$ENDIF} + procedure ReadData_W(Reader: TReader); + procedure ReadDataUTF7(Reader: TReader); + procedure WriteData_W(Writer: TWriter); + function ReadChar(Reader: TReader): WideChar; + public + procedure DefineProperties(Filer: TFiler; Instance: TPersistent; PropName: AnsiString); + end; + +{$IFNDEF COMPILER_9_UP} +type + TGetLookupInfoEvent = procedure(var Ancestor: TPersistent; + var Root, LookupRoot, RootAncestor: TComponent) of object; + +function AncestorIsValid(Ancestor: TPersistent; Root, RootAncestor: TComponent): Boolean; +begin + Result := (Ancestor <> nil) and (RootAncestor <> nil) and + Root.InheritsFrom(RootAncestor.ClassType); +end; + +function IsDefaultOrdPropertyValue(Instance: TObject; PropInfo: PPropInfo; + OnGetLookupInfo: TGetLookupInfoEvent): Boolean; +var + Ancestor: TPersistent; + LookupRoot: TComponent; + RootAncestor: TComponent; + Root: TComponent; + AncestorValid: Boolean; + Value: Longint; + Default: LongInt; +begin + Ancestor := nil; + Root := nil; + LookupRoot := nil; + RootAncestor := nil; + + if Assigned(OnGetLookupInfo) then + OnGetLookupInfo(Ancestor, Root, LookupRoot, RootAncestor); + + AncestorValid := AncestorIsValid(Ancestor, Root, RootAncestor); + + Result := True; + if (PropInfo^.GetProc <> nil) and (PropInfo^.SetProc <> nil) then + begin + Value := GetOrdProp(Instance, PropInfo); + if AncestorValid then + Result := Value = GetOrdProp(Ancestor, PropInfo) + else + begin + Default := PPropInfo(PropInfo)^.Default; + Result := (Default <> LongInt($80000000)) and (Value = Default); + end; + end; +end; + +procedure TTntWideCharPropertyFiler.GetLookupInfo(var Ancestor: TPersistent; + var Root, LookupRoot, RootAncestor: TComponent); +begin + Ancestor := FWriter.Ancestor; + Root := FWriter.Root; + LookupRoot := FWriter.LookupRoot; + RootAncestor := FWriter.RootAncestor; +end; +{$ENDIF} + +function TTntWideCharPropertyFiler.ReadChar(Reader: TReader): WideChar; +var + Temp: WideString; +begin + case Reader.NextValue of + vaWString: + Temp := Reader.ReadWideString; + vaString: + Temp := Reader.ReadString; + else + raise EReadError.Create(SInvalidPropertyValue); + end; + + if Length(Temp) > 1 then + raise EReadError.Create(SInvalidPropertyValue); + Result := Temp[1]; +end; + +procedure TTntWideCharPropertyFiler.ReadData_W(Reader: TReader); +begin + SetOrdProp(FInstance, FPropInfo, Ord(ReadChar(Reader))); +end; + +procedure TTntWideCharPropertyFiler.ReadDataUTF7(Reader: TReader); +var + S: WideString; +begin + S := UTF7ToWideString(Reader.ReadString); + if S = '' then + SetOrdProp(FInstance, FPropInfo, 0) + else + SetOrdProp(FInstance, FPropInfo, Ord(S[1])) +end; + +type TAccessWriter = class(TWriter); + +procedure TTntWideCharPropertyFiler.WriteData_W(Writer: TWriter); +var + L: Integer; + Temp: WideString; +begin + Temp := WideChar(GetOrdProp(FInstance, FPropInfo)); + + {$IFNDEF FPC} + TAccessWriter(Writer).WriteValue(vaWString); + {$ELSE} + TAccessWriter(Writer).Write(vaWString, SizeOf(vaWString)); + {$ENDIF} + L := Length(Temp); + Writer.Write(L, SizeOf(Integer)); + Writer.Write(Pointer(@Temp[1])^, L * 2); +end; + +procedure TTntWideCharPropertyFiler.DefineProperties(Filer: TFiler; + Instance: TPersistent; PropName: AnsiString); + + {$IFNDEF COMPILER_9_UP} + function HasData: Boolean; + var + CurrPropValue: Integer; + begin + // must be stored + Result := IsStoredProp(Instance, FPropInfo); + if Result and (Filer.Ancestor <> nil) and + (GetPropInfo(Filer.Ancestor, PropName, [tkWChar]) <> nil) then + begin + // must be different than ancestor + CurrPropValue := GetOrdProp(Instance, FPropInfo); + Result := CurrPropValue <> GetOrdProp(Filer.Ancestor, GetPropInfo(Filer.Ancestor, PropName)); + end; + if Result and (Filer is TWriter) then + begin + FWriter := TWriter(Filer); + Result := not IsDefaultOrdPropertyValue(Instance, FPropInfo, GetLookupInfo); + end; + end; + {$ENDIF} + +begin + FInstance := Instance; + FPropInfo := GetPropInfo(Instance, PropName, [tkWChar]); + if FPropInfo <> nil then + begin + // must be published (and of type WideChar) + {$IFDEF COMPILER_9_UP} + Filer.DefineProperty(PropName + 'W', ReadData_W, WriteData_W, False); + {$ELSE} + Filer.DefineProperty(PropName + 'W', ReadData_W, WriteData_W, HasData); + {$ENDIF} + Filer.DefineProperty(PropName + '_UTF7', ReadDataUTF7, nil, False); + end; + FInstance := nil; + FPropInfo := nil; +end; + +procedure TntPersistent_AfterInherited_DefineProperties(Filer: TFiler; Instance: TPersistent); +var + I, Count: Integer; + PropInfo: PPropInfo; + PropList: PPropList; + WideStringFiler: TTntWideStringPropertyFiler; + WideCharFiler: TTntWideCharPropertyFiler; +begin + Count := GetTypeData(Instance.ClassInfo)^.PropCount; + if Count > 0 then + begin + WideStringFiler := TTntWideStringPropertyFiler.Create; + try + WideCharFiler := TTntWideCharPropertyFiler.Create; + try + GetMem(PropList, Count * SizeOf(Pointer)); + try + GetPropInfos(Instance.ClassInfo, PropList); + for I := 0 to Count - 1 do + begin + PropInfo := PropList^[I]; + if (PropInfo = nil) then + break; + if (PropInfo.PropType^.Kind = tkWString) then + WideStringFiler.DefineProperties(Filer, Instance, PropInfo.Name) + else if (PropInfo.PropType^.Kind = tkWChar) then + WideCharFiler.DefineProperties(Filer, Instance, PropInfo.Name) + end; + finally + FreeMem(PropList, Count * SizeOf(Pointer)); + end; + finally + WideCharFiler.Free; + end; + finally + WideStringFiler.Free; + end; + end; +end; + +{ TTntFileStream } + +{$IFDEF FPC} + {$DEFINE HAS_SFCREATEERROREX} +{$ENDIF} +{$IFDEF DELPHI_7_UP} + {$DEFINE HAS_SFCREATEERROREX} +{$ENDIF} + +constructor TTntFileStream.Create(const FileName: WideString; Mode: Word); +var + CreateHandle: Integer; + {$IFDEF HAS_SFCREATEERROREX} + ErrorMessage: WideString; + {$ENDIF} +begin + if Mode = fmCreate then + begin + CreateHandle := WideFileCreate(FileName); + if CreateHandle < 0 then begin + {$IFDEF HAS_SFCREATEERROREX} + ErrorMessage := WideSysErrorMessage(GetLastError); + raise EFCreateError.CreateFmt(SFCreateErrorEx, [WideExpandFileName(FileName), ErrorMessage]); + {$ELSE} + raise EFCreateError.CreateFmt(SFCreateError, [WideExpandFileName(FileName)]); + {$ENDIF} + end; + end else + begin + CreateHandle := WideFileOpen(FileName, Mode); + if CreateHandle < 0 then begin + {$IFDEF HAS_SFCREATEERROREX} + ErrorMessage := WideSysErrorMessage(GetLastError); + raise EFOpenError.CreateFmt(SFOpenErrorEx, [WideExpandFileName(FileName), ErrorMessage]); + {$ELSE} + raise EFOpenError.CreateFmt(SFOpenError, [WideExpandFileName(FileName)]); + {$ENDIF} + end; + end; + inherited Create(CreateHandle); +end; + +destructor TTntFileStream.Destroy; +begin + if Handle >= 0 then FileClose(Handle); +end; + +{ TTntMemoryStream } + +procedure TTntMemoryStream.LoadFromFile(const FileName: WideString); +var + Stream: TStream; +begin + Stream := TTntFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite); + try + LoadFromStream(Stream); + finally + Stream.Free; + end; +end; + +procedure TTntMemoryStream.SaveToFile(const FileName: WideString); +var + Stream: TStream; +begin + Stream := TTntFileStream.Create(FileName, fmCreate); + try + SaveToStream(Stream); + finally + Stream.Free; + end; +end; + +{ TTntResourceStream } + +constructor TTntResourceStream.Create(Instance: THandle; const ResName: WideString; + ResType: PWideChar); +begin + inherited Create; + Initialize(Instance, PWideChar(ResName), ResType); +end; + +constructor TTntResourceStream.CreateFromID(Instance: THandle; ResID: Word; + ResType: PWideChar); +begin + inherited Create; + Initialize(Instance, PWideChar(ResID), ResType); +end; + +procedure TTntResourceStream.Initialize(Instance: THandle; Name, ResType: PWideChar); + + procedure Error; + begin + raise EResNotFound.CreateFmt(SResNotFound, [Name]); + end; + +begin + HResInfo := FindResourceW(Instance, Name, ResType); + if HResInfo = 0 then Error; + HGlobal := LoadResource(Instance, HResInfo); + if HGlobal = 0 then Error; + SetPointer(LockResource(HGlobal), SizeOfResource(Instance, HResInfo)); +end; + +destructor TTntResourceStream.Destroy; +begin + UnlockResource(HGlobal); + FreeResource(HGlobal); { Technically this is not necessary (MS KB #193678) } + inherited Destroy; +end; + +function TTntResourceStream.Write(const Buffer; Count: Longint): Longint; +begin + raise EStreamError.CreateRes(PResStringRec(@SCantWriteResourceStreamError)); +end; + +procedure TTntResourceStream.SaveToFile(const FileName: WideString); +var + Stream: TStream; +begin + Stream := TTntFileStream.Create(FileName, fmCreate); + try + SaveToStream(Stream); + finally + Stream.Free; + end; +end; + +{ TAnsiStrings } + +procedure TAnsiStrings{TNT-ALLOW TAnsiStrings}.LoadFromFile(const FileName: WideString); +var + Stream: TStream; +begin + Stream := TTntFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite); + try + LoadFromStream(Stream); + finally + Stream.Free; + end; +end; + +procedure TAnsiStrings{TNT-ALLOW TAnsiStrings}.SaveToFile(const FileName: WideString); +var + Stream: TStream; +begin + Stream := TTntFileStream.Create(FileName, fmCreate); + try + SaveToStream(Stream); + finally + Stream.Free; + end; +end; + +procedure TAnsiStrings{TNT-ALLOW TAnsiStrings}.LoadFromFileEx(const FileName: WideString; CodePage: Cardinal); +var + Stream: TStream; +begin + Stream := TTntFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite); + try + LoadFromStreamEx(Stream, CodePage); + finally + Stream.Free; + end; +end; + +procedure TAnsiStrings{TNT-ALLOW TAnsiStrings}.SaveToFileEx(const FileName: WideString; CodePage: Cardinal); +var + Stream: TStream; + Utf8BomPtr: PAnsiChar; +begin + Stream := TTntFileStream.Create(FileName, fmCreate); + try + if (CodePage = CP_UTF8) then + begin + Utf8BomPtr := PAnsiChar(UTF8_BOM); + Stream.WriteBuffer(Utf8BomPtr^, Length(UTF8_BOM)); + end; + SaveToStreamEx(Stream, CodePage); + finally + Stream.Free; + end; +end; + +{ TAnsiStringsForWideStringsAdapter } + +constructor TAnsiStringsForWideStringsAdapter.Create(AWideStrings: TTntStrings; _AdapterCodePage: Cardinal); +begin + inherited Create; + FWideStrings := AWideStrings; + FAdapterCodePage := _AdapterCodePage; +end; + +function TAnsiStringsForWideStringsAdapter.AdapterCodePage: Cardinal; +begin + if FAdapterCodePage = 0 then + Result := TntSystem.DefaultSystemCodePage + else + Result := FAdapterCodePage; +end; + +procedure TAnsiStringsForWideStringsAdapter.Clear; +begin + FWideStrings.Clear; +end; + +procedure TAnsiStringsForWideStringsAdapter.Delete(Index: Integer); +begin + FWideStrings.Delete(Index); +end; + +function TAnsiStringsForWideStringsAdapter.Get(Index: Integer): AnsiString; +begin + Result := WideStringToStringEx(FWideStrings.Get(Index), AdapterCodePage); +end; + +procedure TAnsiStringsForWideStringsAdapter.Put(Index: Integer; const S: AnsiString); +begin + FWideStrings.Put(Index, StringToWideStringEx(S, AdapterCodePage)); +end; + +function TAnsiStringsForWideStringsAdapter.GetCount: Integer; +begin + Result := FWideStrings.GetCount; +end; + +procedure TAnsiStringsForWideStringsAdapter.Insert(Index: Integer; const S: AnsiString); +begin + FWideStrings.Insert(Index, StringToWideStringEx(S, AdapterCodePage)); +end; + +function TAnsiStringsForWideStringsAdapter.GetObject(Index: Integer): TObject; +begin + Result := FWideStrings.GetObject(Index); +end; + +procedure TAnsiStringsForWideStringsAdapter.PutObject(Index: Integer; AObject: TObject); +begin + FWideStrings.PutObject(Index, AObject); +end; + +procedure TAnsiStringsForWideStringsAdapter.SetUpdateState(Updating: Boolean); +begin + FWideStrings.SetUpdateState(Updating); +end; + +procedure TAnsiStringsForWideStringsAdapter.LoadFromStreamEx(Stream: TStream; CodePage: Cardinal); +var + Size: Integer; + S: AnsiString; +begin + BeginUpdate; + try + Size := Stream.Size - Stream.Position; + SetString(S, nil, Size); + Stream.Read(Pointer(S)^, Size); + FWideStrings.SetTextStr(StringToWideStringEx(S, CodePage)); + finally + EndUpdate; + end; +end; + +procedure TAnsiStringsForWideStringsAdapter.SaveToStreamEx(Stream: TStream; CodePage: Cardinal); +var + S: AnsiString; +begin + S := WideStringToStringEx(FWideStrings.GetTextStr, CodePage); + Stream.WriteBuffer(Pointer(S)^, Length(S)); +end; + +{ TTntStrings } + +constructor TTntStrings.Create; +begin + inherited; + FAnsiStrings := TAnsiStringsForWideStringsAdapter.Create(Self); + FLastFileCharSet := csUnicode; +end; + +destructor TTntStrings.Destroy; +begin + FreeAndNil(FAnsiStrings); + inherited; +end; + +procedure TTntStrings.SetAnsiStrings(const Value: TAnsiStrings{TNT-ALLOW TAnsiStrings}); +begin + FAnsiStrings.Assign(Value); +end; + +procedure TTntStrings.DefineProperties(Filer: TFiler); + + {$IFNDEF COMPILER_7_UP} + function DoWrite: Boolean; + begin + if Filer.Ancestor <> nil then + begin + Result := True; + if Filer.Ancestor is TWideStrings then + Result := not Equals(TWideStrings(Filer.Ancestor)) + end + else Result := Count > 0; + end; + + function DoWriteAsUTF7: Boolean; + var + i: integer; + begin + Result := False; + for i := 0 to Count - 1 do begin + if (Strings[i] <> '') and (WideStringToUTF8(Strings[i]) <> Strings[i]) then begin + Result := True; + break; { found a string with non-ASCII chars (> 127) } + end; + end; + end; + {$ENDIF} + +begin + inherited DefineProperties(Filer); { Handles main 'Strings' property.' } + Filer.DefineProperty('WideStrings', ReadData, nil, False); + Filer.DefineProperty('WideStringsW', ReadDataUTF8, nil, False); + {$IFDEF COMPILER_7_UP} + Filer.DefineProperty('WideStrings_UTF7', ReadDataUTF7, WriteDataUTF7, False); + {$ELSE} + Filer.DefineProperty('WideStrings_UTF7', ReadDataUTF7, WriteDataUTF7, DoWrite and DoWriteAsUTF7); + {$ENDIF} +end; + +procedure TTntStrings.LoadFromFile(const FileName: WideString); +var + Stream: TStream; +begin + Stream := TTntFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite); + try + FLastFileCharSet := AutoDetectCharacterSet(Stream); + Stream.Position := 0; + LoadFromStream(Stream); + finally + Stream.Free; + end; +end; + +procedure TTntStrings.LoadFromStream(Stream: TStream); +begin + LoadFromStream_BOM(Stream, True); +end; + +procedure TTntStrings.LoadFromStream_BOM(Stream: TStream; WithBOM: Boolean); +var + DataLeft: Integer; + StreamCharSet: TTntStreamCharSet; + SW: WideString; + SA: AnsiString; +begin + BeginUpdate; + try + if WithBOM then + StreamCharSet := AutoDetectCharacterSet(Stream) + else + StreamCharSet := csUnicode; + DataLeft := Stream.Size - Stream.Position; + if (StreamCharSet in [csUnicode, csUnicodeSwapped]) then + begin + // BOM indicates Unicode text stream + if DataLeft < SizeOf(WideChar) then + SW := '' + else begin + SetLength(SW, DataLeft div SizeOf(WideChar)); + Stream.Read(PWideChar(SW)^, DataLeft); + if StreamCharSet = csUnicodeSwapped then + StrSwapByteOrder(PWideChar(SW)); + end; + SetTextStr(SW); + end + else if StreamCharSet = csUtf8 then + begin + // BOM indicates UTF-8 text stream + SetLength(SA, DataLeft div SizeOf(AnsiChar)); + Stream.Read(PAnsiChar(SA)^, DataLeft); + SetTextStr(UTF8ToWideString(SA)); + end + else + begin + // without byte order mark it is assumed that we are loading ANSI text + SetLength(SA, DataLeft div SizeOf(AnsiChar)); + Stream.Read(PAnsiChar(SA)^, DataLeft); + SetTextStr(SA); + end; + finally + EndUpdate; + end; +end; + +procedure TTntStrings.ReadData(Reader: TReader); +begin + if Reader.NextValue in [vaString, vaLString] then + SetTextStr(Reader.ReadString) {JCL compatiblity} + else if Reader.NextValue = vaWString then + SetTextStr(Reader.ReadWideString) {JCL compatiblity} + else begin + BeginUpdate; + try + Clear; + Reader.ReadListBegin; + while not Reader.EndOfList do + if Reader.NextValue in [vaString, vaLString] then + Add(Reader.ReadString) {TStrings compatiblity} + else + Add(Reader.ReadWideString); + Reader.ReadListEnd; + finally + EndUpdate; + end; + end; +end; + +procedure TTntStrings.ReadDataUTF7(Reader: TReader); +begin + Reader.ReadListBegin; + if ReaderNeedsUtfHelp(Reader) then + begin + BeginUpdate; + try + Clear; + while not Reader.EndOfList do + Add(UTF7ToWideString(Reader.ReadString)) + finally + EndUpdate; + end; + end else begin + while not Reader.EndOfList do + Reader.ReadString; { do nothing with Result } + end; + Reader.ReadListEnd; +end; + +procedure TTntStrings.ReadDataUTF8(Reader: TReader); +begin + Reader.ReadListBegin; + if ReaderNeedsUtfHelp(Reader) + or (Count = 0){ Legacy support where 'WideStrings' was never written in lieu of WideStringsW } + then begin + BeginUpdate; + try + Clear; + while not Reader.EndOfList do + Add(UTF8ToWideString(Reader.ReadString)) + finally + EndUpdate; + end; + end else begin + while not Reader.EndOfList do + Reader.ReadString; { do nothing with Result } + end; + Reader.ReadListEnd; +end; + +procedure TTntStrings.SaveToFile(const FileName: WideString); +var + Stream: TStream; +begin + Stream := TTntFileStream.Create(FileName, fmCreate); + try + SaveToStream(Stream); + finally + Stream.Free; + end; +end; + +procedure TTntStrings.SaveToStream(Stream: TStream); +begin + SaveToStream_BOM(Stream, True); +end; + +procedure TTntStrings.SaveToStream_BOM(Stream: TStream; WithBOM: Boolean); +// Saves the currently loaded text into the given stream. +// WithBOM determines whether to write a byte order mark or not. +var + SW: WideString; + BOM: WideChar; +begin + if WithBOM then begin + BOM := UNICODE_BOM; + Stream.WriteBuffer(BOM, SizeOf(WideChar)); + end; + SW := GetTextStr; + Stream.WriteBuffer(PWideChar(SW)^, Length(SW) * SizeOf(WideChar)); +end; + +procedure TTntStrings.WriteDataUTF7(Writer: TWriter); +var + I: Integer; +begin + Writer.WriteListBegin; + for I := 0 to Count-1 do + Writer.WriteString(WideStringToUTF7(Get(I))); + Writer.WriteListEnd; +end; + +{ TTntStringList } + +destructor TTntStringList.Destroy; +begin + FOnChange := nil; + FOnChanging := nil; + inherited Destroy; + if FCount <> 0 then Finalize(FList^[0], FCount); + FCount := 0; + SetCapacity(0); +end; + +function TTntStringList.Add(const S: WideString): Integer; +begin + Result := AddObject(S, nil); +end; + +function TTntStringList.AddObject(const S: WideString; AObject: TObject): Integer; +begin + if not Sorted then + Result := FCount + else + if Find(S, Result) then + case Duplicates of + dupIgnore: Exit; + dupError: Error(PResStringRec(@SDuplicateString), 0); + end; + InsertItem(Result, S, AObject); +end; + +procedure TTntStringList.Changed; +begin + if (not FUpdating) and Assigned(FOnChange) then + FOnChange(Self); +end; + +procedure TTntStringList.Changing; +begin + if (not FUpdating) and Assigned(FOnChanging) then + FOnChanging(Self); +end; + +procedure TTntStringList.Clear; +begin + if FCount <> 0 then + begin + Changing; + Finalize(FList^[0], FCount); + FCount := 0; + SetCapacity(0); + Changed; + end; +end; + +procedure TTntStringList.Delete(Index: Integer); +begin + if (Index < 0) or (Index >= FCount) then Error(PResStringRec(@SListIndexError), Index); + Changing; + Finalize(FList^[Index]); + Dec(FCount); + if Index < FCount then + System.Move(FList^[Index + 1], FList^[Index], + (FCount - Index) * SizeOf(TWideStringItem)); + Changed; +end; + +procedure TTntStringList.Exchange(Index1, Index2: Integer); +begin + if (Index1 < 0) or (Index1 >= FCount) then Error(PResStringRec(@SListIndexError), Index1); + if (Index2 < 0) or (Index2 >= FCount) then Error(PResStringRec(@SListIndexError), Index2); + Changing; + ExchangeItems(Index1, Index2); + Changed; +end; + +procedure TTntStringList.ExchangeItems(Index1, Index2: Integer); +var + Temp: Integer; + Item1, Item2: PWideStringItem; +begin + Item1 := @FList^[Index1]; + Item2 := @FList^[Index2]; + Temp := Integer(Item1^.FString); + Integer(Item1^.FString) := Integer(Item2^.FString); + Integer(Item2^.FString) := Temp; + Temp := Integer(Item1^.FObject); + Integer(Item1^.FObject) := Integer(Item2^.FObject); + Integer(Item2^.FObject) := Temp; +end; + +function TTntStringList.Find(const S: WideString; var Index: Integer): Boolean; +var + L, H, I, C: Integer; +begin + Result := False; + L := 0; + H := FCount - 1; + while L <= H do + begin + I := (L + H) shr 1; + C := CompareStrings(FList^[I].FString, S); + if C < 0 then L := I + 1 else + begin + H := I - 1; + if C = 0 then + begin + Result := True; + if Duplicates <> dupAccept then L := I; + end; + end; + end; + Index := L; +end; + +function TTntStringList.Get(Index: Integer): WideString; +begin + if (Index < 0) or (Index >= FCount) then Error(PResStringRec(@SListIndexError), Index); + Result := FList^[Index].FString; +end; + +function TTntStringList.GetCapacity: Integer; +begin + Result := FCapacity; +end; + +function TTntStringList.GetCount: Integer; +begin + Result := FCount; +end; + +function TTntStringList.GetObject(Index: Integer): TObject; +begin + if (Index < 0) or (Index >= FCount) then Error(PResStringRec(@SListIndexError), Index); + Result := FList^[Index].FObject; +end; + +procedure TTntStringList.Grow; +var + Delta: Integer; +begin + if FCapacity > 64 then Delta := FCapacity div 4 else + if FCapacity > 8 then Delta := 16 else + Delta := 4; + SetCapacity(FCapacity + Delta); +end; + +function TTntStringList.IndexOf(const S: WideString): Integer; +begin + if not Sorted then Result := inherited IndexOf(S) else + if not Find(S, Result) then Result := -1; +end; + +function TTntStringList.IndexOfName(const Name: WideString): Integer; +var + NameKey: WideString; +begin + if not Sorted then + Result := inherited IndexOfName(Name) + else begin + // use sort to find index more quickly + NameKey := Name + NameValueSeparator; + Find(NameKey, Result); + if (Result < 0) or (Result > Count - 1) then + Result := -1 + else if CompareStrings(NameKey, Copy(Strings[Result], 1, Length(NameKey))) <> 0 then + Result := -1 + end; +end; + +procedure TTntStringList.Insert(Index: Integer; const S: WideString); +begin + InsertObject(Index, S, nil); +end; + +procedure TTntStringList.InsertObject(Index: Integer; const S: WideString; + AObject: TObject); +begin + if Sorted then Error(PResStringRec(@SSortedListError), 0); + if (Index < 0) or (Index > FCount) then Error(PResStringRec(@SListIndexError), Index); + InsertItem(Index, S, AObject); +end; + +procedure TTntStringList.InsertItem(Index: Integer; const S: WideString; AObject: TObject); +begin + Changing; + if FCount = FCapacity then Grow; + if Index < FCount then + System.Move(FList^[Index], FList^[Index + 1], + (FCount - Index) * SizeOf(TWideStringItem)); + with FList^[Index] do + begin + Pointer(FString) := nil; + FObject := AObject; + FString := S; + end; + Inc(FCount); + Changed; +end; + +procedure TTntStringList.Put(Index: Integer; const S: WideString); +begin + if Sorted then Error(PResStringRec(@SSortedListError), 0); + if (Index < 0) or (Index >= FCount) then Error(PResStringRec(@SListIndexError), Index); + Changing; + FList^[Index].FString := S; + Changed; +end; + +procedure TTntStringList.PutObject(Index: Integer; AObject: TObject); +begin + if (Index < 0) or (Index >= FCount) then Error(PResStringRec(@SListIndexError), Index); + Changing; + FList^[Index].FObject := AObject; + Changed; +end; + +procedure TTntStringList.QuickSort(L, R: Integer; SCompare: TWideStringListSortCompare); +var + I, J, P: Integer; +begin + repeat + I := L; + J := R; + P := (L + R) shr 1; + repeat + while SCompare(Self, I, P) < 0 do Inc(I); + while SCompare(Self, J, P) > 0 do Dec(J); + if I <= J then + begin + ExchangeItems(I, J); + if P = I then + P := J + else if P = J then + P := I; + Inc(I); + Dec(J); + end; + until I > J; + if L < J then QuickSort(L, J, SCompare); + L := I; + until I >= R; +end; + +procedure TTntStringList.SetCapacity(NewCapacity: Integer); +begin + ReallocMem(FList, NewCapacity * SizeOf(TWideStringItem)); + FCapacity := NewCapacity; +end; + +procedure TTntStringList.SetSorted(Value: Boolean); +begin + if FSorted <> Value then + begin + if Value then Sort; + FSorted := Value; + end; +end; + +procedure TTntStringList.SetUpdateState(Updating: Boolean); +begin + FUpdating := Updating; + if Updating then Changing else Changed; +end; + +function WideStringListCompareStrings(List: TTntStringList; Index1, Index2: Integer): Integer; +begin + Result := List.CompareStrings(List.FList^[Index1].FString, + List.FList^[Index2].FString); +end; + +procedure TTntStringList.Sort; +begin + CustomSort(WideStringListCompareStrings); +end; + +procedure TTntStringList.CustomSort(Compare: TWideStringListSortCompare); +begin + if not Sorted and (FCount > 1) then + begin + Changing; + QuickSort(0, FCount - 1, Compare); + Changed; + end; +end; + +function TTntStringList.CompareStrings(const S1, S2: WideString): Integer; +begin + if CaseSensitive then + Result := WideCompareStr(S1, S2) + else + Result := WideCompareText(S1, S2); +end; + +procedure TTntStringList.SetCaseSensitive(const Value: Boolean); +begin + if Value <> FCaseSensitive then + begin + FCaseSensitive := Value; + if Sorted then Sort; + end; +end; + +//------------------------- TntClasses introduced procs ---------------------------------- + +function AutoDetectCharacterSet(Stream: TStream): TTntStreamCharSet; +var + ByteOrderMark: WideChar; + BytesRead: Integer; + Utf8Test: array[0..2] of AnsiChar; +begin + // Byte Order Mark + ByteOrderMark := #0; + if (Stream.Size - Stream.Position) >= SizeOf(ByteOrderMark) then begin + BytesRead := Stream.Read(ByteOrderMark, SizeOf(ByteOrderMark)); + if (ByteOrderMark <> UNICODE_BOM) and (ByteOrderMark <> UNICODE_BOM_SWAPPED) then begin + ByteOrderMark := #0; + Stream.Seek(-BytesRead, soFromCurrent); + if (Stream.Size - Stream.Position) >= Length(Utf8Test) * SizeOf(AnsiChar) then begin + BytesRead := Stream.Read(Utf8Test[0], Length(Utf8Test) * SizeOf(AnsiChar)); + if Utf8Test <> UTF8_BOM then + Stream.Seek(-BytesRead, soFromCurrent); + end; + end; + end; + // Test Byte Order Mark + if ByteOrderMark = UNICODE_BOM then + Result := csUnicode + else if ByteOrderMark = UNICODE_BOM_SWAPPED then + Result := csUnicodeSwapped + else if Utf8Test = UTF8_BOM then + Result := csUtf8 + else + Result := csAnsi; +end; + +function FindSortedListByTarget(List: TList; TargetCompare: TListTargetCompare; + Target: Pointer; var Index: Integer): Boolean; +var + L, H, I, C: Integer; +begin + Result := False; + L := 0; + H := List.Count - 1; + while L <= H do + begin + I := (L + H) shr 1; + C := TargetCompare(List[i], Target); + if C < 0 then L := I + 1 else + begin + H := I - 1; + if C = 0 then + begin + Result := True; + L := I; + end; + end; + end; + Index := L; +end; + +function ClassIsRegistered(const clsid: TCLSID): Boolean; +var + OleStr: POleStr; + Reg: TRegIniFile; + Key, Filename: WideString; +begin + // First, check to see if there is a ProgID. This will tell if the + // control is registered on the machine. No ProgID, control won't run + Result := ProgIDFromCLSID(clsid, OleStr) = S_OK; + if not Result then Exit; //Bail as soon as anything goes wrong. + + // Next, make sure that the file is actually there by rooting it out + // of the registry + Key := WideFormat('\SOFTWARE\Classes\CLSID\%s', [GUIDToString(clsid)]); + Reg := TRegIniFile.Create; + try + Reg.RootKey := HKEY_LOCAL_MACHINE; + Result := Reg.OpenKeyReadOnly(Key); + if not Result then Exit; // Bail as soon as anything goes wrong. + + FileName := Reg.ReadString('InProcServer32', '', EmptyStr); + if (Filename = EmptyStr) then // try another key for the file name + begin + FileName := Reg.ReadString('InProcServer', '', EmptyStr); + end; + Result := Filename <> EmptyStr; + if not Result then Exit; + Result := WideFileExists(Filename); + finally + Reg.Free; + end; +end; + +{ TBufferedAnsiString } + +procedure TBufferedAnsiString.Clear; +begin + LastWriteIndex := 0; + if Length(FStringBuffer) > 0 then + FillChar(FStringBuffer[1], Length(FStringBuffer) * SizeOf(AnsiChar), 0); +end; + +procedure TBufferedAnsiString.AddChar(const wc: AnsiChar); +const + MIN_GROW_SIZE = 32; + MAX_GROW_SIZE = 256; +var + GrowSize: Integer; +begin + Inc(LastWriteIndex); + if LastWriteIndex > Length(FStringBuffer) then begin + GrowSize := Max(MIN_GROW_SIZE, Length(FStringBuffer)); + GrowSize := Min(GrowSize, MAX_GROW_SIZE); + SetLength(FStringBuffer, Length(FStringBuffer) + GrowSize); + FillChar(FStringBuffer[LastWriteIndex], GrowSize * SizeOf(AnsiChar), 0); + end; + FStringBuffer[LastWriteIndex] := wc; +end; + +procedure TBufferedAnsiString.AddString(const s: AnsiString); +var + LenS: Integer; + BlockSize: Integer; + AllocSize: Integer; +begin + LenS := Length(s); + if LenS > 0 then begin + Inc(LastWriteIndex); + if LastWriteIndex + LenS - 1 > Length(FStringBuffer) then begin + // determine optimum new allocation size + BlockSize := Length(FStringBuffer) div 2; + if BlockSize < 8 then + BlockSize := 8; + AllocSize := ((LenS div BlockSize) + 1) * BlockSize; + // realloc buffer + SetLength(FStringBuffer, Length(FStringBuffer) + AllocSize); + FillChar(FStringBuffer[Length(FStringBuffer) - AllocSize + 1], AllocSize * SizeOf(AnsiChar), 0); + end; + CopyMemory(@FStringBuffer[LastWriteIndex], @s[1], LenS * SizeOf(AnsiChar)); + Inc(LastWriteIndex, LenS - 1); + end; +end; + +procedure TBufferedAnsiString.AddBuffer(Buff: PAnsiChar; Chars: Integer); +var + i: integer; +begin + for i := 1 to Chars do begin + if Buff^ = #0 then + break; + AddChar(Buff^); + Inc(Buff); + end; +end; + +function TBufferedAnsiString.Value: AnsiString; +begin + Result := PAnsiChar(FStringBuffer); +end; + +function TBufferedAnsiString.BuffPtr: PAnsiChar; +begin + Result := PAnsiChar(FStringBuffer); +end; + +{ TBufferedWideString } + +procedure TBufferedWideString.Clear; +begin + LastWriteIndex := 0; + if Length(FStringBuffer) > 0 then + FillChar(FStringBuffer[1], Length(FStringBuffer) * SizeOf(WideChar), 0); +end; + +procedure TBufferedWideString.AddChar(const wc: WideChar); +const + MIN_GROW_SIZE = 32; + MAX_GROW_SIZE = 256; +var + GrowSize: Integer; +begin + Inc(LastWriteIndex); + if LastWriteIndex > Length(FStringBuffer) then begin + GrowSize := Max(MIN_GROW_SIZE, Length(FStringBuffer)); + GrowSize := Min(GrowSize, MAX_GROW_SIZE); + SetLength(FStringBuffer, Length(FStringBuffer) + GrowSize); + FillChar(FStringBuffer[LastWriteIndex], GrowSize * SizeOf(WideChar), 0); + end; + FStringBuffer[LastWriteIndex] := wc; +end; + +procedure TBufferedWideString.AddString(const s: WideString); +var + i: integer; +begin + for i := 1 to Length(s) do + AddChar(s[i]); +end; + +procedure TBufferedWideString.AddBuffer(Buff: PWideChar; Chars: Integer); +var + i: integer; +begin + for i := 1 to Chars do begin + if Buff^ = #0 then + break; + AddChar(Buff^); + Inc(Buff); + end; +end; + +function TBufferedWideString.Value: WideString; +begin + Result := PWideChar(FStringBuffer); +end; + +function TBufferedWideString.BuffPtr: PWideChar; +begin + Result := PWideChar(FStringBuffer); +end; + +{ TBufferedStreamReader } + +constructor TBufferedStreamReader.Create(Stream: TStream; BufferSize: Integer = 1024); +begin + // init stream + FStream := Stream; + FStreamSize := Stream.Size; + // init buffer + FBufferSize := BufferSize; + SetLength(FBuffer, BufferSize); + FBufferStartPosition := -FBufferSize; { out of any useful range } + // init virtual position + FVirtualPosition := 0; +end; + +function TBufferedStreamReader.Seek(Offset: Integer; Origin: Word): Longint; +begin + case Origin of + soFromBeginning: FVirtualPosition := Offset; + soFromCurrent: Inc(FVirtualPosition, Offset); + soFromEnd: FVirtualPosition := FStreamSize + Offset; + end; + Result := FVirtualPosition; +end; + +procedure TBufferedStreamReader.UpdateBufferFromPosition(StartPos: Integer); +begin + try + FStream.Position := StartPos; + FStream.Read(FBuffer[0], FBufferSize); + FBufferStartPosition := StartPos; + except + FBufferStartPosition := -FBufferSize; { out of any useful range } + raise; + end; +end; + +function TBufferedStreamReader.Read(var Buffer; Count: Integer): Longint; +var + BytesLeft: Integer; + FirstBufferRead: Integer; + StreamDirectRead: Integer; + Buf: PAnsiChar; +begin + if (FVirtualPosition >= 0) and (Count >= 0) then + begin + Result := FStreamSize - FVirtualPosition; + if Result > 0 then + begin + if Result > Count then + Result := Count; + + Buf := @Buffer; + BytesLeft := Result; + + // try to read what is left in buffer + FirstBufferRead := FBufferStartPosition + FBufferSize - FVirtualPosition; + if (FirstBufferRead < 0) or (FirstBufferRead > FBufferSize) then + FirstBufferRead := 0; + FirstBufferRead := Min(FirstBufferRead, Result); + if FirstBufferRead > 0 then begin + Move(FBuffer[FVirtualPosition - FBufferStartPosition], Buf[0], FirstBufferRead); + Dec(BytesLeft, FirstBufferRead); + end; + + if BytesLeft > 0 then begin + // The first read in buffer was not enough + StreamDirectRead := (BytesLeft div FBufferSize) * FBufferSize; + FStream.Position := FVirtualPosition + FirstBufferRead; + FStream.Read(Buf[FirstBufferRead], StreamDirectRead); + Dec(BytesLeft, StreamDirectRead); + + if BytesLeft > 0 then begin + // update buffer, and read what is left + UpdateBufferFromPosition(FStream.Position); + Move(FBuffer[0], Buf[FirstBufferRead + StreamDirectRead], BytesLeft); + end; + end; + + Inc(FVirtualPosition, Result); + Exit; + end; + end; + Result := 0; +end; + +function TBufferedStreamReader.Write(const Buffer; Count: Integer): Longint; +begin + raise ETntInternalError.Create('Internal Error: class can not write.'); + Result := 0; +end; + +//-------- synced wide string ----------------- + +function GetSyncedWideString(var WideStr: WideString; const AnsiStr: AnsiString): WideString; +begin + if AnsiString(WideStr) <> (AnsiStr) then begin + WideStr := AnsiStr; {AnsiStr changed. Keep WideStr in sync.} + end; + Result := WideStr; +end; + +procedure SetSyncedWideString(const Value: WideString; var WideStr: WideString; + const AnsiStr: AnsiString; SetAnsiStr: TSetAnsiStrEvent); +begin + if Value <> GetSyncedWideString(WideStr, AnsiStr) then + begin + if (not WideSameStr(Value, AnsiString(Value))) {unicode chars lost in conversion} + and (AnsiStr = AnsiString(Value)) {AnsiStr is not going to change} + then begin + SetAnsiStr(''); {force the change} + end; + WideStr := Value; + SetAnsiStr(Value); + end; +end; + +{ TWideComponentHelper } + +function CompareComponentHelperToTarget(Item, Target: Pointer): Integer; +begin + if PtrUInt(TWideComponentHelper(Item).FComponent) < PtrUInt(Target) then + Result := -1 + else if PtrUInt(TWideComponentHelper(Item).FComponent) > PtrUInt(Target) then + Result := 1 + else + Result := 0; +end; + +function FindWideComponentHelperIndex(ComponentHelperList: TComponentList; Component: TComponent; var Index: Integer): Boolean; +begin + // find Component in sorted wide caption list (list is sorted by TWideComponentHelper.FComponent) + Result := FindSortedListByTarget(ComponentHelperList, CompareComponentHelperToTarget, Component, Index); +end; + +constructor TWideComponentHelper.Create(AOwner: TComponent); +begin + raise ETntInternalError.Create('TNT Internal Error: TWideComponentHelper.Create should never be encountered.'); +end; + +constructor TWideComponentHelper.CreateHelper(AOwner: TComponent; ComponentHelperList: TComponentList); +var + Index: Integer; +begin + // don't use direct ownership for memory management + inherited Create(nil); + FComponent := AOwner; + FComponent.FreeNotification(Self); + + // insert into list according to sort + FindWideComponentHelperIndex(ComponentHelperList, FComponent, Index); + ComponentHelperList.Insert(Index, Self); +end; + +procedure TWideComponentHelper.Notification(AComponent: TComponent; Operation: TOperation); +begin + inherited; + if (AComponent = FComponent) and (Operation = opRemove) then begin + FComponent := nil; + Free; + end; +end; + +function FindWideComponentHelper(ComponentHelperList: TComponentList; Component: TComponent): TWideComponentHelper; +var + Index: integer; +begin + if FindWideComponentHelperIndex(ComponentHelperList, Component, Index) then begin + Result := TWideComponentHelper(ComponentHelperList[Index]); + Assert(Result.FComponent = Component, 'TNT Internal Error: FindWideComponentHelperIndex failed.'); + end else + Result := nil; +end; + +initialization + RuntimeUTFStreaming := False; { Delphi 6 and higher don't need UTF help at runtime. } + +end. diff --git a/Lua/src/lib/TntUnicodeControls/TntCompilers.inc b/Lua/src/lib/TntUnicodeControls/TntCompilers.inc new file mode 100644 index 00000000..06f4d9ab --- /dev/null +++ b/Lua/src/lib/TntUnicodeControls/TntCompilers.inc @@ -0,0 +1,378 @@ +//---------------------------------------------------------------------------------------------------------------------- +// Include file to determine which compiler is currently being used to build the project/component. +// This file uses ideas from Brad Stowers DFS.inc file (www.delphifreestuff.com). +// +// Portions created by Mike Lischke are Copyright +// (C) 1999-2002 Dipl. Ing. Mike Lischke. All Rights Reserved. +//---------------------------------------------------------------------------------------------------------------------- +// The following symbols are defined: +// +// COMPILER_1 : Kylix/Delphi/BCB 1.x is the compiler. +// COMPILER_1_UP : Kylix/Delphi/BCB 1.x or higher is the compiler. +// COMPILER_2 : Kylix/Delphi 2.x or BCB 1.x is the compiler. +// COMPILER_2_UP : Kylix/Delphi 2.x or higher, or BCB 1.x or higher is the compiler. +// COMPILER_3 : Kylix/Delphi/BCB 3.x is the compiler. +// COMPILER_3_UP : Kylix/Delphi/BCB 3.x or higher is the compiler. +// COMPILER_4 : Kylix/Delphi/BCB 4.x is the compiler. +// COMPILER_4_UP : Kylix/Delphi/BCB 4.x or higher is the compiler. +// COMPILER_5 : Kylix/Delphi/BCB 5.x is the compiler. +// COMPILER_5_UP : Kylix/Delphi/BCB 5.x or higher is the compiler. +// COMPILER_6 : Kylix/Delphi/BCB 6.x is the compiler. +// COMPILER_6_UP : Kylix/Delphi/BCB 6.x or higher is the compiler. +// COMPILER_7 : Kylix/Delphi/BCB 7.x is the compiler. +// COMPILER_7_UP : Kylix/Delphi/BCB 7.x or higher is the compiler. +// +// Only defined if Windows is the target: +// CPPB : Any version of BCB is being used. +// CPPB_1 : BCB v1.x is being used. +// CPPB_3 : BCB v3.x is being used. +// CPPB_3_UP : BCB v3.x or higher is being used. +// CPPB_4 : BCB v4.x is being used. +// CPPB_4_UP : BCB v4.x or higher is being used. +// CPPB_5 : BCB v5.x is being used. +// CPPB_5_UP : BCB v5.x or higher is being used. +// CPPB_6 : BCB v6.x is being used. +// CPPB_6_UP : BCB v6.x or higher is being used. +// +// Only defined if Windows is the target: +// DELPHI : Any version of Delphi is being used. +// DELPHI_1 : Delphi v1.x is being used. +// DELPHI_2 : Delphi v2.x is being used. +// DELPHI_2_UP : Delphi v2.x or higher is being used. +// DELPHI_3 : Delphi v3.x is being used. +// DELPHI_3_UP : Delphi v3.x or higher is being used. +// DELPHI_4 : Delphi v4.x is being used. +// DELPHI_4_UP : Delphi v4.x or higher is being used. +// DELPHI_5 : Delphi v5.x is being used. +// DELPHI_5_UP : Delphi v5.x or higher is being used. +// DELPHI_6 : Delphi v6.x is being used. +// DELPHI_6_UP : Delphi v6.x or higher is being used. +// DELPHI_7 : Delphi v7.x is being used. +// DELPHI_7_UP : Delphi v7.x or higher is being used. +// +// Only defined if Linux is the target: +// KYLIX : Any version of Kylix is being used. +// KYLIX_1 : Kylix 1.x is being used. +// KYLIX_1_UP : Kylix 1.x or higher is being used. +// KYLIX_2 : Kylix 2.x is being used. +// KYLIX_2_UP : Kylix 2.x or higher is being used. +// KYLIX_3 : Kylix 3.x is being used. +// KYLIX_3_UP : Kylix 3.x or higher is being used. +// +// Only defined if Linux is the target: +// QT_CLX : Trolltech's QT library is being used. +//---------------------------------------------------------------------------------------------------------------------- + +{$ifdef Win32} + + {$ifdef VER180} + {$define COMPILER_10} + {$define DELPHI} + {$define DELPHI_10} + {$endif} + + {$ifdef VER170} + {$define COMPILER_9} + {$define DELPHI} + {$define DELPHI_9} + {$endif} + + {$ifdef VER150} + {$define COMPILER_7} + {$define DELPHI} + {$define DELPHI_7} + {$endif} + + {$ifdef VER140} + {$define COMPILER_6} + {$ifdef BCB} + {$define CPPB} + {$define CPPB_6} + {$else} + {$define DELPHI} + {$define DELPHI_6} + {$endif} + {$endif} + + {$ifdef VER130} + {$define COMPILER_5} + {$ifdef BCB} + {$define CPPB} + {$define CPPB_5} + {$else} + {$define DELPHI} + {$define DELPHI_5} + {$endif} + {$endif} + + {$ifdef VER125} + {$define COMPILER_4} + {$define CPPB} + {$define CPPB_4} + {$endif} + + {$ifdef VER120} + {$define COMPILER_4} + {$define DELPHI} + {$define DELPHI_4} + {$endif} + + {$ifdef VER110} + {$define COMPILER_3} + {$define CPPB} + {$define CPPB_3} + {$endif} + + {$ifdef VER100} + {$define COMPILER_3} + {$define DELPHI} + {$define DELPHI_3} + {$endif} + + {$ifdef VER93} + {$define COMPILER_2} // C++ Builder v1 compiler is really v2 + {$define CPPB} + {$define CPPB_1} + {$endif} + + {$ifdef VER90} + {$define COMPILER_2} + {$define DELPHI} + {$define DELPHI_2} + {$endif} + + {$ifdef VER80} + {$define COMPILER_1} + {$define DELPHI} + {$define DELPHI_1} + {$endif} + + {$ifdef FPC} + {.$define DELPHI} + {$endif} + + {$ifdef DELPHI_2} + {$define DELPHI_2_UP} + {$endif} + + {$ifdef DELPHI_3} + {$define DELPHI_2_UP} + {$define DELPHI_3_UP} + {$endif} + + {$ifdef DELPHI_4} + {$define DELPHI_2_UP} + {$define DELPHI_3_UP} + {$define DELPHI_4_UP} + {$endif} + + {$ifdef DELPHI_5} + {$define DELPHI_2_UP} + {$define DELPHI_3_UP} + {$define DELPHI_4_UP} + {$define DELPHI_5_UP} + {$endif} + + {$ifdef DELPHI_6} + {$define DELPHI_2_UP} + {$define DELPHI_3_UP} + {$define DELPHI_4_UP} + {$define DELPHI_5_UP} + {$define DELPHI_6_UP} + {$endif} + + {$ifdef DELPHI_7} + {$define DELPHI_2_UP} + {$define DELPHI_3_UP} + {$define DELPHI_4_UP} + {$define DELPHI_5_UP} + {$define DELPHI_6_UP} + {$define DELPHI_7_UP} + {$endif} + + {$ifdef DELPHI_9} + {$define DELPHI_2_UP} + {$define DELPHI_3_UP} + {$define DELPHI_4_UP} + {$define DELPHI_5_UP} + {$define DELPHI_6_UP} + {$define DELPHI_7_UP} + {$define DELPHI_9_UP} + {$endif} + + {$ifdef DELPHI_10} + {$define DELPHI_2_UP} + {$define DELPHI_3_UP} + {$define DELPHI_4_UP} + {$define DELPHI_5_UP} + {$define DELPHI_6_UP} + {$define DELPHI_7_UP} + {$define DELPHI_9_UP} + {$define DELPHI_10_UP} + {$endif} + + {$ifdef CPPB_3} + {$define CPPB_3_UP} + {$endif} + + {$ifdef CPPB_4} + {$define CPPB_3_UP} + {$define CPPB_4_UP} + {$endif} + + {$ifdef CPPB_5} + {$define CPPB_3_UP} + {$define CPPB_4_UP} + {$define CPPB_5_UP} + {$endif} + + {$ifdef CPPB_6} + {$define CPPB_3_UP} + {$define CPPB_4_UP} + {$define CPPB_5_UP} + {$define CPPB_6_UP} + {$endif} + + {$ifdef CPPB_3_UP} + // C++ Builder requires this if you use Delphi components in run-time packages. + {$ObjExportAll On} + {$endif} + +{$else (not Windows)} + // Linux is the target + {$define QT_CLX} + + {$define KYLIX} + {$define KYLIX_1} + {$define KYLIX_1_UP} + + {$ifdef VER150} + {$define COMPILER_7} + {$define KYLIX_3} + {$endif} + + {$ifdef VER140} + {$define COMPILER_6} + {$define KYLIX_2} + {$endif} + + {$ifdef KYLIX_2} + {$define KYLIX_2_UP} + {$endif} + + {$ifdef KYLIX_3} + {$define KYLIX_2_UP} + {$define KYLIX_3_UP} + {$endif} + +{$endif} + +// Compiler defines common to all platforms. +{$ifdef COMPILER_1} + {$define COMPILER_1_UP} +{$endif} + +{$ifdef COMPILER_2} + {$define COMPILER_1_UP} + {$define COMPILER_2_UP} +{$endif} + +{$ifdef COMPILER_3} + {$define COMPILER_1_UP} + {$define COMPILER_2_UP} + {$define COMPILER_3_UP} +{$endif} + +{$ifdef COMPILER_4} + {$define COMPILER_1_UP} + {$define COMPILER_2_UP} + {$define COMPILER_3_UP} + {$define COMPILER_4_UP} +{$endif} + +{$ifdef COMPILER_5} + {$define COMPILER_1_UP} + {$define COMPILER_2_UP} + {$define COMPILER_3_UP} + {$define COMPILER_4_UP} + {$define COMPILER_5_UP} +{$endif} + +{$ifdef COMPILER_6} + {$define COMPILER_1_UP} + {$define COMPILER_2_UP} + {$define COMPILER_3_UP} + {$define COMPILER_4_UP} + {$define COMPILER_5_UP} + {$define COMPILER_6_UP} +{$endif} + +{$ifdef COMPILER_7} + {$define COMPILER_1_UP} + {$define COMPILER_2_UP} + {$define COMPILER_3_UP} + {$define COMPILER_4_UP} + {$define COMPILER_5_UP} + {$define COMPILER_6_UP} + {$define COMPILER_7_UP} +{$endif} + +{$ifdef COMPILER_9} + {$define COMPILER_1_UP} + {$define COMPILER_2_UP} + {$define COMPILER_3_UP} + {$define COMPILER_4_UP} + {$define COMPILER_5_UP} + {$define COMPILER_6_UP} + {$define COMPILER_7_UP} + {$define COMPILER_9_UP} +{$endif} + +{$ifdef COMPILER_10} + {$define COMPILER_1_UP} + {$define COMPILER_2_UP} + {$define COMPILER_3_UP} + {$define COMPILER_4_UP} + {$define COMPILER_5_UP} + {$define COMPILER_6_UP} + {$define COMPILER_7_UP} + {$define COMPILER_9_UP} + {$define COMPILER_10_UP} +{$endif} + +//---------------------------------------------------------------------------------------------------------------------- + +{$ALIGN ON} +{$BOOLEVAL OFF} + +{$ifdef COMPILER_7_UP} + {$define THEME_7_UP} { Allows experimental theme support on pre-Delphi 7. } +{$endif} + +{$IFDEF COMPILER_6_UP} +{$WARN SYMBOL_PLATFORM OFF} { We are going to use Win32 specific symbols! } +{$ENDIF} + +{$IFDEF COMPILER_7_UP} +{$IFDEF FPC} + {$DEFINE UNSAFE_WARNINGS_OFF} +{$ENDIF} +{$ENDIF} + +{$IFDEF UNSAFE_WARNINGS_OFF} +{$WARN UNSAFE_CODE OFF} { We are not going to be "safe"! } +{$WARN UNSAFE_TYPE OFF} +{$WARN UNSAFE_CAST OFF} +{$ENDIF} + +{$IFDEF FPC} +{$HINTS OFF} +{$ENDIF} + +{$IFNDEF FPC} + // Delphi system function overrides might (not tested) cause problems on + // CPUs with code protection (NX-bit). So disable by default. + {.$DEFINE USE_SYSTEM_OVERRIDES} +{$ENDIF} + + diff --git a/Lua/src/lib/TntUnicodeControls/TntFormatStrUtils.pas b/Lua/src/lib/TntUnicodeControls/TntFormatStrUtils.pas new file mode 100644 index 00000000..c6b65082 --- /dev/null +++ b/Lua/src/lib/TntUnicodeControls/TntFormatStrUtils.pas @@ -0,0 +1,521 @@ + +{*****************************************************************************} +{ } +{ Tnt Delphi Unicode Controls } +{ http://www.tntware.com/delphicontrols/unicode/ } +{ Version: 2.3.0 } +{ } +{ Copyright (c) 2002-2007, Troy Wolbrink (troy.wolbrink@tntware.com) } +{ } +{*****************************************************************************} + +unit TntFormatStrUtils; + +{$IFDEF FPC} + {$MODE Delphi} +{$ENDIF} + +{$INCLUDE TntCompilers.inc} + +interface + +// this unit provides functions to work with format strings + +uses + TntSysUtils; + +function GetCanonicalFormatStr(const _FormatString: WideString): WideString; + +{$IFNDEF FPC} +{$IFNDEF COMPILER_9_UP} +function ReplaceFloatingArgumentsInFormatString(const _FormatString: WideString; + const Args: array of const + {$IFDEF COMPILER_7_UP}; FormatSettings: PFormatSettings{$ENDIF}): WideString; +{$ENDIF} +{$ENDIF} +procedure CompareFormatStrings(FormatStr1, FormatStr2: WideString); +function FormatStringsAreCompatible(FormatStr1, FormatStr2: WideString): Boolean; + +type + EFormatSpecError = class(ETntGeneralError); + +implementation + +uses + SysUtils, Math, TntClasses; + +resourcestring + SInvalidFormatSpecifier = 'Invalid Format Specifier: %s'; + SMismatchedArgumentTypes = 'Argument types for index %d do not match. (%s <> %s)'; + SMismatchedArgumentCounts = 'Number of format specifiers do not match.'; + +type + TFormatSpecifierType = (fstInteger, fstFloating, fstPointer, fstString); + +function GetFormatSpecifierType(const FormatSpecifier: WideString): TFormatSpecifierType; +var + LastChar: WideChar; +begin + LastChar := TntWideLastChar(FormatSpecifier); + case LastChar of + 'd', 'D', 'u', 'U', 'x', 'X': + result := fstInteger; + 'e', 'E', 'f', 'F', 'g', 'G', 'n', 'N', 'm', 'M': + result := fstFloating; + 'p', 'P': + result := fstPointer; + 's', 'S': + result := fstString + else + raise ETntInternalError.CreateFmt('Internal Error: Unexpected format type (%s)', [LastChar]); + end; +end; + +type + TFormatStrParser = class(TObject) + private + ParsedString: TBufferedWideString; + PFormatString: PWideChar; + LastIndex: Integer; + ExplicitCount: Integer; + ImplicitCount: Integer; + procedure RaiseInvalidFormatSpecifier; + function ParseChar(c: WideChar): Boolean; + procedure ForceParseChar(c: WideChar); + function ParseDigit: Boolean; + function ParseInteger: Boolean; + procedure ForceParseType; + function PeekDigit: Boolean; + function PeekIndexSpecifier(out Index: Integer): Boolean; + public + constructor Create(const _FormatString: WideString); + destructor Destroy; override; + function ParseFormatSpecifier: Boolean; + end; + +constructor TFormatStrParser.Create(const _FormatString: WideString); +begin + inherited Create; + PFormatString := PWideChar(_FormatString); + ExplicitCount := 0; + ImplicitCount := 0; + LastIndex := -1; + ParsedString := TBufferedWideString.Create; +end; + +destructor TFormatStrParser.Destroy; +begin + FreeAndNil(ParsedString); + inherited; +end; + +procedure TFormatStrParser.RaiseInvalidFormatSpecifier; +begin + raise EFormatSpecError.CreateFmt(SInvalidFormatSpecifier, [ParsedString.Value + PFormatString]); +end; + +function TFormatStrParser.ParseChar(c: WideChar): Boolean; +begin + result := False; + if PFormatString^ = c then begin + result := True; + ParsedString.AddChar(c); + Inc(PFormatString); + end; +end; + +procedure TFormatStrParser.ForceParseChar(c: WideChar); +begin + if not ParseChar(c) then + RaiseInvalidFormatSpecifier; +end; + +function TFormatStrParser.PeekDigit: Boolean; +begin + result := False; + if (PFormatString^ <> #0) + and (PFormatString^ >= '0') + and (PFormatString^ <= '9') then + result := True; +end; + +function TFormatStrParser.ParseDigit: Boolean; +begin + result := False; + if PeekDigit then begin + result := True; + ForceParseChar(PFormatString^); + end; +end; + +function TFormatStrParser.ParseInteger: Boolean; +const + MAX_INT_DIGITS = 6; +var + digitcount: integer; +begin + digitcount := 0; + While ParseDigit do begin + inc(digitcount); + end; + result := (digitcount > 0); + if digitcount > MAX_INT_DIGITS then + RaiseInvalidFormatSpecifier; +end; + +procedure TFormatStrParser.ForceParseType; +begin + if PFormatString^ = #0 then + RaiseInvalidFormatSpecifier; + + case PFormatString^ of + 'd', 'u', 'x', 'e', 'f', 'g', 'n', 'm', 'p', 's', + 'D', 'U', 'X', 'E', 'F', 'G', 'N', 'M', 'P', 'S': + begin + // do nothing + end + else + RaiseInvalidFormatSpecifier; + end; + ForceParseChar(PFormatString^); +end; + +function TFormatStrParser.PeekIndexSpecifier(out Index: Integer): Boolean; +var + SaveParsedString: WideString; + SaveFormatString: PWideChar; +begin + SaveParsedString := ParsedString.Value; + SaveFormatString := PFormatString; + try + ParsedString.Clear; + Result := False; + Index := -1; + if ParseInteger then begin + Index := StrToInt(ParsedString.Value); + if ParseChar(':') then + Result := True; + end; + finally + ParsedString.Clear; + ParsedString.AddString(SaveParsedString); + PFormatString := SaveFormatString; + end; +end; + +function TFormatStrParser.ParseFormatSpecifier: Boolean; +var + ExplicitIndex: Integer; +begin + Result := False; + // Parse entire format specifier + ForceParseChar('%'); + if (PFormatString^ <> #0) + and (not ParseChar(' ')) + and (not ParseChar('%')) then begin + if PeekIndexSpecifier(ExplicitIndex) then begin + Inc(ExplicitCount); + LastIndex := Max(LastIndex, ExplicitIndex); + end else begin + Inc(ImplicitCount); + Inc(LastIndex); + ParsedString.AddString(IntToStr(LastIndex)); + ParsedString.AddChar(':'); + end; + if ParseChar('*') then + begin + Inc(ImplicitCount); + Inc(LastIndex); + ParseChar(':'); + end else if ParseInteger then + ParseChar(':'); + ParseChar('-'); + if ParseChar('*') then begin + Inc(ImplicitCount); + Inc(LastIndex); + end else + ParseInteger; + if ParseChar('.') then begin + if not ParseChar('*') then + ParseInteger; + end; + ForceParseType; + Result := True; + end; +end; + +//----------------------------------- + +function GetCanonicalFormatStr(const _FormatString: WideString): WideString; +var + PosSpec: Integer; +begin + with TFormatStrParser.Create(_FormatString) do + try + // loop until no more '%' + PosSpec := Pos('%', PFormatString); + While PosSpec <> 0 do begin + try + // delete everything up until '%' + ParsedString.AddBuffer(PFormatString, PosSpec - 1); + Inc(PFormatString, PosSpec - 1); + // parse format specifier + ParseFormatSpecifier; + finally + PosSpec := Pos('%', PFormatString); + end; + end; + if ((ExplicitCount = 0) and (ImplicitCount = 1)) {simple expression} + or ((ExplicitCount > 0) and (ImplicitCount = 0)) {nothing converted} then + result := _FormatString {original} + else + result := ParsedString.Value + PFormatString; + finally + Free; + end; +end; + +{$IFNDEF FPC} +{$IFNDEF COMPILER_9_UP} +function ReplaceFloatingArgumentsInFormatString(const _FormatString: WideString; + const Args: array of const + {$IFDEF COMPILER_7_UP}; FormatSettings: PFormatSettings{$ENDIF}): WideString; +{ This function replaces floating point format specifiers with their actual formatted values. + It also adds index specifiers so that the other format specifiers don't lose their place. + The reason for this is that WideFormat doesn't correctly format floating point specifiers. + See QC#4254. } +var + Parser: TFormatStrParser; + PosSpec: Integer; + Output: TBufferedWideString; +begin + Output := TBufferedWideString.Create; + try + Parser := TFormatStrParser.Create(_FormatString); + with Parser do + try + // loop until no more '%' + PosSpec := Pos('%', PFormatString); + While PosSpec <> 0 do begin + try + // delete everything up until '%' + Output.AddBuffer(PFormatString, PosSpec - 1); + Inc(PFormatString, PosSpec - 1); + // parse format specifier + ParsedString.Clear; + if (not ParseFormatSpecifier) + or (GetFormatSpecifierType(ParsedString.Value) <> fstFloating) then + Output.AddBuffer(ParsedString.BuffPtr, MaxInt) + {$IFDEF COMPILER_7_UP} + else if Assigned(FormatSettings) then + Output.AddString(Format{TNT-ALLOW Format}(ParsedString.Value, Args, FormatSettings^)) + {$ENDIF} + else + Output.AddString(Format{TNT-ALLOW Format}(ParsedString.Value, Args)); + finally + PosSpec := Pos('%', PFormatString); + end; + end; + Output.AddString(PFormatString); + finally + Free; + end; + Result := Output.Value; + finally + Output.Free; + end; +end; +{$ENDIF} +{$ENDIF} + +procedure GetFormatArgs(const _FormatString: WideString; FormatArgs: TTntStrings); +var + PosSpec: Integer; +begin + with TFormatStrParser.Create(_FormatString) do + try + FormatArgs.Clear; + // loop until no more '%' + PosSpec := Pos('%', PFormatString); + While PosSpec <> 0 do begin + try + // delete everything up until '%' + Inc(PFormatString, PosSpec - 1); + // add format specifier to list + ParsedString.Clear; + if ParseFormatSpecifier then + FormatArgs.Add(ParsedString.Value); + finally + PosSpec := Pos('%', PFormatString); + end; + end; + finally + Free; + end; +end; + +function GetExplicitIndex(const FormatSpecifier: WideString): Integer; +var + IndexStr: WideString; + PosColon: Integer; +begin + result := -1; + PosColon := Pos(':', FormatSpecifier); + if PosColon <> 0 then begin + IndexStr := Copy(FormatSpecifier, 2, PosColon - 2); + result := StrToInt(IndexStr); + end; +end; + +function GetMaxIndex(FormatArgs: TTntStrings): Integer; +var + i: integer; + RunningIndex: Integer; + ExplicitIndex: Integer; +begin + result := -1; + RunningIndex := -1; + for i := 0 to FormatArgs.Count - 1 do begin + ExplicitIndex := GetExplicitIndex(FormatArgs[i]); + if ExplicitIndex <> -1 then + RunningIndex := ExplicitIndex + else + inc(RunningIndex); + result := Max(result, RunningIndex); + end; +end; + +function FormatSpecToObject(SpecType: TFormatSpecifierType): TObject; +begin + {$IFNDEF FPC} + Result := TObject(SpecType); + {$ELSE} + Result := Pointer(SpecType); + {$ENDIF} +end; + +procedure UpdateTypeList(FormatArgs, TypeList: TTntStrings); +var + i: integer; + f: WideString; + SpecType: TFormatSpecifierType; + ExplicitIndex: Integer; + MaxIndex: Integer; + RunningIndex: Integer; +begin + // set count of TypeList to accomodate maximum index + MaxIndex := GetMaxIndex(FormatArgs); + TypeList.Clear; + for i := 0 to MaxIndex do + TypeList.Add(''); + + // for each arg... + RunningIndex := -1; + for i := 0 to FormatArgs.Count - 1 do begin + f := FormatArgs[i]; + ExplicitIndex := GetExplicitIndex(f); + SpecType := GetFormatSpecifierType(f); + + // determine running arg index + if ExplicitIndex <> -1 then + RunningIndex := ExplicitIndex + else + inc(RunningIndex); + + if TypeList[RunningIndex] <> '' then begin + // already exists in list, check for compatibility + if TypeList.Objects[RunningIndex] <> FormatSpecToObject(SpecType) then + raise EFormatSpecError.CreateFmt(SMismatchedArgumentTypes, + [RunningIndex, TypeList[RunningIndex], f]); + end else begin + // not in list so update it + TypeList[RunningIndex] := f; + TypeList.Objects[RunningIndex] := FormatSpecToObject(SpecType); + end; + end; +end; + +procedure CompareFormatStrings(FormatStr1, FormatStr2: WideString); +var + ArgList1: TTntStringList; + ArgList2: TTntStringList; + TypeList1: TTntStringList; + TypeList2: TTntStringList; + i: integer; +begin + ArgList1 := nil; + ArgList2 := nil; + TypeList1 := nil; + TypeList2 := nil; + try + ArgList1 := TTntStringList.Create; + ArgList2 := TTntStringList.Create; + TypeList1 := TTntStringList.Create; + TypeList2 := TTntStringList.Create; + + GetFormatArgs(FormatStr1, ArgList1); + UpdateTypeList(ArgList1, TypeList1); + + GetFormatArgs(FormatStr2, ArgList2); + UpdateTypeList(ArgList2, TypeList2); + + if TypeList1.Count <> TypeList2.Count then + raise EFormatSpecError.Create(SMismatchedArgumentCounts + CRLF + CRLF + '> ' + FormatStr1 + CRLF + '> ' + FormatStr2); + + for i := 0 to TypeList1.Count - 1 do begin + if TypeList1.Objects[i] <> TypeList2.Objects[i] then begin + raise EFormatSpecError.CreateFmt(SMismatchedArgumentTypes, + [i, TypeList1[i], TypeList2[i]]); + end; + end; + + finally + ArgList1.Free; + ArgList2.Free; + TypeList1.Free; + TypeList2.Free; + end; +end; + +function FormatStringsAreCompatible(FormatStr1, FormatStr2: WideString): Boolean; +var + ArgList1: TTntStringList; + ArgList2: TTntStringList; + TypeList1: TTntStringList; + TypeList2: TTntStringList; + i: integer; +begin + ArgList1 := nil; + ArgList2 := nil; + TypeList1 := nil; + TypeList2 := nil; + try + ArgList1 := TTntStringList.Create; + ArgList2 := TTntStringList.Create; + TypeList1 := TTntStringList.Create; + TypeList2 := TTntStringList.Create; + + GetFormatArgs(FormatStr1, ArgList1); + UpdateTypeList(ArgList1, TypeList1); + + GetFormatArgs(FormatStr2, ArgList2); + UpdateTypeList(ArgList2, TypeList2); + + Result := (TypeList1.Count = TypeList2.Count); + if Result then begin + for i := 0 to TypeList1.Count - 1 do begin + if TypeList1.Objects[i] <> TypeList2.Objects[i] then begin + Result := False; + break; + end; + end; + end; + finally + ArgList1.Free; + ArgList2.Free; + TypeList1.Free; + TypeList2.Free; + end; +end; + +end. diff --git a/Lua/src/lib/TntUnicodeControls/TntSysUtils.pas b/Lua/src/lib/TntUnicodeControls/TntSysUtils.pas new file mode 100644 index 00000000..b7cf2467 --- /dev/null +++ b/Lua/src/lib/TntUnicodeControls/TntSysUtils.pas @@ -0,0 +1,1753 @@ + +{*****************************************************************************} +{ } +{ Tnt Delphi Unicode Controls } +{ http://www.tntware.com/delphicontrols/unicode/ } +{ Version: 2.3.0 } +{ } +{ Copyright (c) 2002-2007, Troy Wolbrink (troy.wolbrink@tntware.com) } +{ } +{*****************************************************************************} + +unit TntSysUtils; + +{$IFDEF FPC} + {$MODE Delphi} +{$ENDIF} + +{$INCLUDE TntCompilers.inc} + +interface + +{ TODO: Consider: more filename functions from SysUtils } +{ TODO: Consider: string functions from StrUtils. } + +uses + Types, SysUtils, Windows, TntWindows; + +//--------------------------------------------------------------------------------------------- +// Tnt - Types +//--------------------------------------------------------------------------------------------- + +// ......... introduced ......... +type + // The user of the application did something plainly wrong. + ETntUserError = class(Exception); + // A general error occured. (ie. file didn't exist, server didn't return data, etc.) + ETntGeneralError = class(Exception); + // Like Assert(). An error occured that should never have happened, send me a bug report now! + ETntInternalError = class(Exception); + +{$IFNDEF FPC} +type + PtrInt = LongInt; + PtrUInt = LongWord; +{$ENDIF} + +//--------------------------------------------------------------------------------------------- +// Tnt - SysUtils +//--------------------------------------------------------------------------------------------- + +// ......... SBCS and MBCS functions with WideString replacements in SysUtils.pas ......... + +{TNT-WARN CompareStr} {TNT-WARN AnsiCompareStr} +{TNT-WARN SameStr} {TNT-WARN AnsiSameStr} +{TNT-WARN SameText} {TNT-WARN AnsiSameText} +{TNT-WARN CompareText} {TNT-WARN AnsiCompareText} +{TNT-WARN UpperCase} {TNT-WARN AnsiUpperCase} +{TNT-WARN LowerCase} {TNT-WARN AnsiLowerCase} + +{TNT-WARN AnsiPos} { --> Pos() supports WideString. } +{TNT-WARN FmtStr} +{TNT-WARN Format} +{TNT-WARN FormatBuf} + +// ......... MBCS Byte Type Procs ......... + +{TNT-WARN ByteType} +{TNT-WARN StrByteType} +{TNT-WARN ByteToCharIndex} +{TNT-WARN ByteToCharLen} +{TNT-WARN CharToByteIndex} +{TNT-WARN CharToByteLen} + +// ........ null-terminated string functions ......... + +{TNT-WARN StrEnd} +{TNT-WARN StrLen} +{TNT-WARN StrLCopy} +{TNT-WARN StrCopy} +{TNT-WARN StrECopy} +{TNT-WARN StrPLCopy} +{TNT-WARN StrPCopy} +{TNT-WARN StrLComp} +{TNT-WARN AnsiStrLComp} +{TNT-WARN StrComp} +{TNT-WARN AnsiStrComp} +{TNT-WARN StrLIComp} +{TNT-WARN AnsiStrLIComp} +{TNT-WARN StrIComp} +{TNT-WARN AnsiStrIComp} +{TNT-WARN StrLower} +{TNT-WARN AnsiStrLower} +{TNT-WARN StrUpper} +{TNT-WARN AnsiStrUpper} +{TNT-WARN StrPos} +{TNT-WARN AnsiStrPos} +{TNT-WARN StrScan} +{TNT-WARN AnsiStrScan} +{TNT-WARN StrRScan} +{TNT-WARN AnsiStrRScan} +{TNT-WARN StrLCat} +{TNT-WARN StrCat} +{TNT-WARN StrMove} +{TNT-WARN StrPas} +{TNT-WARN StrAlloc} +{TNT-WARN StrBufSize} +{TNT-WARN StrNew} +{TNT-WARN StrDispose} + +{TNT-WARN AnsiExtractQuotedStr} +{TNT-WARN AnsiLastChar} +{TNT-WARN AnsiStrLastChar} +{TNT-WARN QuotedStr} +{TNT-WARN AnsiQuotedStr} +{TNT-WARN AnsiDequotedStr} + +// ........ string functions ......... + +{$IFNDEF FPC} +{$IFNDEF COMPILER_9_UP} + // + // pre-Delphi 9 issues w/ WideFormatBuf, WideFmtStr and WideFormat + // + + {$IFDEF COMPILER_7_UP} + type + PFormatSettings = ^TFormatSettings; + {$ENDIF} + + // SysUtils.WideFormatBuf doesn't correctly handle numeric specifiers. + function Tnt_WideFormatBuf(var Buffer; BufLen: Cardinal; const FormatStr; + FmtLen: Cardinal; const Args: array of const): Cardinal; {$IFDEF COMPILER_7_UP} overload; {$ENDIF} + + {$IFDEF COMPILER_7_UP} + function Tnt_WideFormatBuf(var Buffer; BufLen: Cardinal; const FormatStr; + FmtLen: Cardinal; const Args: array of const; + const FormatSettings: TFormatSettings): Cardinal; overload; + {$ENDIF} + + // SysUtils.WideFmtStr doesn't handle string lengths > 4096. + procedure Tnt_WideFmtStr(var Result: WideString; const FormatStr: WideString; + const Args: array of const); {$IFDEF COMPILER_7_UP} overload; {$ENDIF} + + {$IFDEF COMPILER_7_UP} + procedure Tnt_WideFmtStr(var Result: WideString; const FormatStr: WideString; + const Args: array of const; const FormatSettings: TFormatSettings); overload; + {$ENDIF} + + {---------------------------------------------------------------------------------------- + Without the FormatSettings parameter, Tnt_WideFormat is *NOT* necessary... + TntSystem.InstallTntSystemUpdates([tsFixWideFormat]); + will fix WideFormat as well as WideFmtStr. + ----------------------------------------------------------------------------------------} + function Tnt_WideFormat(const FormatStr: WideString; const Args: array of const): WideString; {$IFDEF COMPILER_7_UP} overload; {$ENDIF} + + {$IFDEF COMPILER_7_UP} + function Tnt_WideFormat(const FormatStr: WideString; const Args: array of const; + const FormatSettings: TFormatSettings): WideString; overload; + {$ENDIF} + +{$ENDIF} +{$ENDIF} + +{TNT-WARN WideUpperCase} // SysUtils.WideUpperCase is broken on Win9x for D6, D7, D9. +function Tnt_WideUpperCase(const S: WideString): WideString; +{TNT-WARN WideLowerCase} // SysUtils.WideLowerCase is broken on Win9x for D6, D7, D9. +function Tnt_WideLowerCase(const S: WideString): WideString; + +function TntWideLastChar(const S: WideString): WideChar; + +{TNT-WARN StringReplace} +{TNT-WARN WideStringReplace} // <-- WideStrUtils.WideStringReplace uses SysUtils.WideUpperCase which is broken on Win9x. +function Tnt_WideStringReplace(const S, OldPattern, NewPattern: WideString; + Flags: TReplaceFlags; WholeWord: Boolean = False): WideString; + +{TNT-WARN AdjustLineBreaks} +type TTntTextLineBreakStyle = (tlbsLF, tlbsCRLF, tlbsCR); +function TntAdjustLineBreaksLength(const S: WideString; Style: TTntTextLineBreakStyle = tlbsCRLF): Integer; +function TntAdjustLineBreaks(const S: WideString; Style: TTntTextLineBreakStyle = tlbsCRLF): WideString; + +{TNT-WARN WrapText} +function WideWrapText(const Line, BreakStr: WideString; const BreakChars: TSysCharSet; + MaxCol: Integer): WideString; overload; +function WideWrapText(const Line: WideString; MaxCol: Integer): WideString; overload; + +// ........ filename manipulation ......... + +{TNT-WARN SameFileName} // doesn't apply to Unicode filenames, use WideSameText +{TNT-WARN AnsiCompareFileName} // doesn't apply to Unicode filenames, use WideCompareText +{TNT-WARN AnsiLowerCaseFileName} // doesn't apply to Unicode filenames, use WideLowerCase +{TNT-WARN AnsiUpperCaseFileName} // doesn't apply to Unicode filenames, use WideUpperCase + +{TNT-WARN IncludeTrailingBackslash} +function WideIncludeTrailingBackslash(const S: WideString): WideString; +{TNT-WARN IncludeTrailingPathDelimiter} +function WideIncludeTrailingPathDelimiter(const S: WideString): WideString; +{TNT-WARN ExcludeTrailingBackslash} +function WideExcludeTrailingBackslash(const S: WideString): WideString; +{TNT-WARN ExcludeTrailingPathDelimiter} +function WideExcludeTrailingPathDelimiter(const S: WideString): WideString; +{TNT-WARN IsDelimiter} +function WideIsDelimiter(const Delimiters, S: WideString; Index: Integer): Boolean; +{TNT-WARN IsPathDelimiter} +function WideIsPathDelimiter(const S: WideString; Index: Integer): Boolean; +{TNT-WARN LastDelimiter} +function WideLastDelimiter(const Delimiters, S: WideString): Integer; +{TNT-WARN ChangeFileExt} +function WideChangeFileExt(const FileName, Extension: WideString): WideString; +{TNT-WARN ExtractFilePath} +function WideExtractFilePath(const FileName: WideString): WideString; +{TNT-WARN ExtractFileDir} +function WideExtractFileDir(const FileName: WideString): WideString; +{TNT-WARN ExtractFileDrive} +function WideExtractFileDrive(const FileName: WideString): WideString; +{TNT-WARN ExtractFileName} +function WideExtractFileName(const FileName: WideString): WideString; +{TNT-WARN ExtractFileExt} +function WideExtractFileExt(const FileName: WideString): WideString; +{TNT-WARN ExtractRelativePath} +function WideExtractRelativePath(const BaseName, DestName: WideString): WideString; + +// ........ file management routines ......... + +{TNT-WARN ExpandFileName} +function WideExpandFileName(const FileName: WideString): WideString; +{TNT-WARN ExtractShortPathName} +function WideExtractShortPathName(const FileName: WideString): WideString; +{TNT-WARN FileCreate} +function WideFileCreate(const FileName: WideString): Integer; +{TNT-WARN FileOpen} +function WideFileOpen(const FileName: WideString; Mode: LongWord): Integer; +{TNT-WARN FileAge} +function WideFileAge(const FileName: WideString): Integer; overload; +function WideFileAge(const FileName: WideString; out FileDateTime: TDateTime): Boolean; overload; +{TNT-WARN DirectoryExists} +function WideDirectoryExists(const Name: WideString): Boolean; +{TNT-WARN FileExists} +function WideFileExists(const Name: WideString): Boolean; +{TNT-WARN FileGetAttr} +function WideFileGetAttr(const FileName: WideString): Cardinal; +{TNT-WARN FileSetAttr} +function WideFileSetAttr(const FileName: WideString; Attr: Integer): Boolean; +{TNT-WARN FileIsReadOnly} +function WideFileIsReadOnly(const FileName: WideString): Boolean; +{TNT-WARN FileSetReadOnly} +function WideFileSetReadOnly(const FileName: WideString; ReadOnly: Boolean): Boolean; +{TNT-WARN ForceDirectories} +function WideForceDirectories(Dir: WideString): Boolean; +{TNT-WARN FileSearch} +function WideFileSearch(const Name, DirList: WideString): WideString; +{TNT-WARN RenameFile} +function WideRenameFile(const OldName, NewName: WideString): Boolean; +{TNT-WARN DeleteFile} +function WideDeleteFile(const FileName: WideString): Boolean; +{TNT-WARN CopyFile} +function WideCopyFile(const FromFile, ToFile: WideString; FailIfExists: Boolean): Boolean; + + +{TNT-WARN TFileName} +type + TWideFileName = type WideString; + +{TNT-WARN TSearchRec} // <-- FindFile - warning on TSearchRec is all that is necessary +type + TSearchRecW = record + Time: Integer; + Size: Int64; + Attr: Integer; + Name: TWideFileName; + ExcludeAttr: Integer; + FindHandle: THandle; + FindData: TWin32FindDataW; + end; +function WideFindFirst(const Path: WideString; Attr: Integer; var F: TSearchRecW): Integer; +function WideFindNext(var F: TSearchRecW): Integer; +procedure WideFindClose(var F: TSearchRecW); + +{TNT-WARN CreateDir} +function WideCreateDir(const Dir: WideString): Boolean; +{TNT-WARN RemoveDir} +function WideRemoveDir(const Dir: WideString): Boolean; +{TNT-WARN GetCurrentDir} +function WideGetCurrentDir: WideString; +{TNT-WARN SetCurrentDir} +function WideSetCurrentDir(const Dir: WideString): Boolean; + + +// ........ date/time functions ......... + +{TNT-WARN TryStrToDateTime} +function TntTryStrToDateTime(Str: WideString; out DateTime: TDateTime): Boolean; +{TNT-WARN TryStrToDate} +function TntTryStrToDate(Str: WideString; out DateTime: TDateTime): Boolean; +{TNT-WARN TryStrToTime} +function TntTryStrToTime(Str: WideString; out DateTime: TDateTime): Boolean; + +{ introduced } +function ValidDateTimeStr(Str: WideString): Boolean; +function ValidDateStr(Str: WideString): Boolean; +function ValidTimeStr(Str: WideString): Boolean; + +{TNT-WARN StrToDateTime} +function TntStrToDateTime(Str: WideString): TDateTime; +{TNT-WARN StrToDate} +function TntStrToDate(Str: WideString): TDateTime; +{TNT-WARN StrToTime} +function TntStrToTime(Str: WideString): TDateTime; +{TNT-WARN StrToDateTimeDef} +function TntStrToDateTimeDef(Str: WideString; Default: TDateTime): TDateTime; +{TNT-WARN StrToDateDef} +function TntStrToDateDef(Str: WideString; Default: TDateTime): TDateTime; +{TNT-WARN StrToTimeDef} +function TntStrToTimeDef(Str: WideString; Default: TDateTime): TDateTime; + +{TNT-WARN CurrToStr} +{TNT-WARN CurrToStrF} +function TntCurrToStr(Value: Currency; lpFormat: PCurrencyFmtW = nil): WideString; +{TNT-WARN StrToCurr} +function TntStrToCurr(const S: WideString): Currency; +{TNT-WARN StrToCurrDef} +function ValidCurrencyStr(const S: WideString): Boolean; +function TntStrToCurrDef(const S: WideString; const Default: Currency): Currency; +function GetDefaultCurrencyFmt: TCurrencyFmtW; + +// ........ misc functions ......... + +{TNT-WARN GetLocaleStr} +function WideGetLocaleStr(LocaleID: LCID; LocaleType: Integer; const Default: WideString): WideString; +{TNT-WARN SysErrorMessage} +function WideSysErrorMessage(ErrorCode: Integer): WideString; + +// ......... introduced ......... + +function WideLibraryErrorMessage(const LibName: WideString; Dll: THandle; ErrorCode: Integer): WideString; + +const + CR = WideChar(#13); + LF = WideChar(#10); + CRLF = WideString(#13#10); + WideLineSeparator = WideChar($2028); + +var + Win32PlatformIsUnicode: Boolean; + Win32PlatformIsXP: Boolean; + Win32PlatformIs2003: Boolean; + Win32PlatformIsVista: Boolean; + +{$IFNDEF FPC} +{$IFNDEF COMPILER_7_UP} +function CheckWin32Version(AMajor: Integer; AMinor: Integer = 0): Boolean; +{$ENDIF} +{$ENDIF} +function WinCheckH(RetVal: Cardinal): Cardinal; +function WinCheckFileH(RetVal: Cardinal): Cardinal; +function WinCheckP(RetVal: Pointer): Pointer; + +function WideGetModuleFileName(Instance: HModule): WideString; +function WideSafeLoadLibrary(const Filename: Widestring; + ErrorMode: UINT = SEM_NOOPENFILEERRORBOX): HMODULE; +{$IFNDEF FPC} +function WideLoadPackage(const Name: Widestring): HMODULE; +{$ENDIF} + +function IsWideCharUpper(WC: WideChar): Boolean; +function IsWideCharLower(WC: WideChar): Boolean; +function IsWideCharDigit(WC: WideChar): Boolean; +function IsWideCharSpace(WC: WideChar): Boolean; +function IsWideCharPunct(WC: WideChar): Boolean; +function IsWideCharCntrl(WC: WideChar): Boolean; +function IsWideCharBlank(WC: WideChar): Boolean; +function IsWideCharXDigit(WC: WideChar): Boolean; +function IsWideCharAlpha(WC: WideChar): Boolean; +function IsWideCharAlphaNumeric(WC: WideChar): Boolean; + +function WideTextPos(const SubStr, S: WideString): Integer; + +function ExtractStringArrayStr(P: PWideChar): WideString; +function ExtractStringFromStringArray(var P: PWideChar; Separator: WideChar = #0): WideString; +function ExtractStringsFromStringArray(P: PWideChar; Separator: WideChar = #0): TWideStringDynArray; + +function IsWideCharMappableToAnsi(const WC: WideChar): Boolean; +function IsWideStringMappableToAnsi(const WS: WideString): Boolean; +function IsRTF(const Value: WideString): Boolean; + +function ENG_US_FloatToStr(Value: Extended): WideString; +function ENG_US_StrToFloat(const S: WideString): Extended; + +//--------------------------------------------------------------------------------------------- +// Tnt - Variants +//--------------------------------------------------------------------------------------------- + +// ........ Variants.pas has WideString versions of these functions ......... +{TNT-WARN VarToStr} +{TNT-WARN VarToStrDef} + +var + _SettingChangeTime: Cardinal; + +implementation + +uses + ActiveX, ComObj, SysConst, + {$IFDEF COMPILER_9_UP} WideStrUtils, {$ENDIF} TntWideStrUtils, + TntSystem, TntFormatStrUtils; + +//--------------------------------------------------------------------------------------------- +// Tnt - SysUtils +//--------------------------------------------------------------------------------------------- + +{$IFNDEF FPC} +{$IFNDEF COMPILER_9_UP} + + function _Tnt_WideFormatBuf(var Buffer; BufLen: Cardinal; const FormatStr; + FmtLen: Cardinal; const Args: array of const + {$IFDEF COMPILER_7_UP}; const FormatSettings: PFormatSettings {$ENDIF}): Cardinal; + var + OldFormat: WideString; + NewFormat: WideString; + begin + SetString(OldFormat, PWideChar(@FormatStr), FmtLen); + { The reason for this is that WideFormat doesn't correctly format floating point specifiers. + See QC#4254. } + NewFormat := ReplaceFloatingArgumentsInFormatString(OldFormat, Args{$IFDEF COMPILER_7_UP}, FormatSettings{$ENDIF}); + {$IFDEF COMPILER_7_UP} + if FormatSettings <> nil then + Result := WideFormatBuf(Buffer, BufLen, Pointer(NewFormat)^, + Length(NewFormat), Args, FormatSettings^) + else + {$ENDIF} + Result := WideFormatBuf(Buffer, BufLen, Pointer(NewFormat)^, + Length(NewFormat), Args); + end; + + function Tnt_WideFormatBuf(var Buffer; BufLen: Cardinal; const FormatStr; + FmtLen: Cardinal; const Args: array of const): Cardinal; + begin + Result := _Tnt_WideFormatBuf(Buffer, BufLen, FormatStr, FmtLen, Args{$IFDEF COMPILER_7_UP}, nil{$ENDIF}); + end; + + {$IFDEF COMPILER_7_UP} + function Tnt_WideFormatBuf(var Buffer; BufLen: Cardinal; const FormatStr; + FmtLen: Cardinal; const Args: array of const; const FormatSettings: TFormatSettings): Cardinal; + begin + Result := _Tnt_WideFormatBuf(Buffer, BufLen, FormatStr, FmtLen, Args, @FormatSettings); + end; + {$ENDIF} + + procedure _Tnt_WideFmtStr(var Result: WideString; const FormatStr: WideString; + const Args: array of const{$IFDEF COMPILER_7_UP}; const FormatSettings: PFormatSettings{$ENDIF}); + var + Len, BufLen: Integer; + Buffer: array[0..4095] of WideChar; + begin + BufLen := Length(Buffer); // Fixes buffer overwrite issue. (See QC #4703, #4744) + if Length(FormatStr) < (Length(Buffer) - (Length(Buffer) div 4)) then + Len := _Tnt_WideFormatBuf(Buffer, Length(Buffer) - 1, Pointer(FormatStr)^, + Length(FormatStr), Args{$IFDEF COMPILER_7_UP}, FormatSettings{$ENDIF}) + else + begin + BufLen := Length(FormatStr); + Len := BufLen; + end; + if Len >= BufLen - 1 then + begin + while Len >= BufLen - 1 do + begin + Inc(BufLen, BufLen); + Result := ''; // prevent copying of existing data, for speed + SetLength(Result, BufLen); + Len := _Tnt_WideFormatBuf(Pointer(Result)^, BufLen - 1, Pointer(FormatStr)^, + Length(FormatStr), Args{$IFDEF COMPILER_7_UP}, FormatSettings{$ENDIF}); + end; + SetLength(Result, Len); + end + else + SetString(Result, Buffer, Len); + end; + + procedure Tnt_WideFmtStr(var Result: WideString; const FormatStr: WideString; + const Args: array of const); + begin + _Tnt_WideFmtStr(Result, FormatStr, Args{$IFDEF COMPILER_7_UP}, nil{$ENDIF}); + end; + + {$IFDEF COMPILER_7_UP} + procedure Tnt_WideFmtStr(var Result: WideString; const FormatStr: WideString; + const Args: array of const; const FormatSettings: TFormatSettings); + begin + _Tnt_WideFmtStr(Result, FormatStr, Args, @FormatSettings); + end; + {$ENDIF} + + {---------------------------------------------------------------------------------------- + Without the FormatSettings parameter, Tnt_WideFormat is *NOT* necessary... + TntSystem.InstallTntSystemUpdates([tsFixWideFormat]); + will fix WideFormat as well as WideFmtStr. + ----------------------------------------------------------------------------------------} + function Tnt_WideFormat(const FormatStr: WideString; const Args: array of const): WideString; + begin + Tnt_WideFmtStr(Result, FormatStr, Args); + end; + + {$IFDEF COMPILER_7_UP} + function Tnt_WideFormat(const FormatStr: WideString; const Args: array of const; + const FormatSettings: TFormatSettings): WideString; + begin + Tnt_WideFmtStr(Result, FormatStr, Args, FormatSettings); + end; + {$ENDIF} + +{$ENDIF} +{$ENDIF FPC} + +function Tnt_WideUpperCase(const S: WideString): WideString; +begin + {$IFNDEF FPC} + {$IFNDEF COMPILER_10_UP} + {$DEFINE WIDEUPPERCASE_BROKEN} + {$ENDIF} + {$ENDIF} + {$IFDEF WIDEUPPERCASE_BROKEN} + { SysUtils.WideUpperCase is broken for Win9x. } + Result := S; + if Length(Result) > 0 then + Tnt_CharUpperBuffW(PWideChar(Result), Length(Result)); + {$ELSE} + Result := SysUtils.WideUpperCase{TNT-ALLOW WideUpperCase}(S); + {$ENDIF} +end; + +function Tnt_WideLowerCase(const S: WideString): WideString; +begin + {$IFNDEF FPC} + {$IFNDEF COMPILER_10_UP} + {$DEFINE WIDELOWERCASE_BROKEN} + {$ENDIF} + {$ENDIF} + {$IFDEF WIDELOWERCASE_BROKEN} + { SysUtils.WideLowerCase is broken for Win9x. } + Result := S; + if Length(Result) > 0 then + Tnt_CharLowerBuffW(PWideChar(Result), Length(Result)); + {$ELSE} + Result := SysUtils.WideLowerCase{TNT-ALLOW WideLowerCase}(S); + {$ENDIF} +end; + +function TntWideLastChar(const S: WideString): WideChar; +var + P: PWideChar; +begin + P := WideLastChar(S); + if P = nil then + Result := #0 + else + Result := P^; +end; + +function Tnt_WideStringReplace(const S, OldPattern, NewPattern: WideString; + Flags: TReplaceFlags; WholeWord: Boolean = False): WideString; + + function IsWordSeparator(WC: WideChar): Boolean; + begin + Result := (WC = WideChar(#0)) + or IsWideCharSpace(WC) + or IsWideCharPunct(WC); + end; + +var + SearchStr, Patt, NewStr: WideString; + Offset: Integer; + PrevChar, NextChar: WideChar; +begin + if rfIgnoreCase in Flags then + begin + SearchStr := Tnt_WideUpperCase(S); + Patt := Tnt_WideUpperCase(OldPattern); + end else + begin + SearchStr := S; + Patt := OldPattern; + end; + NewStr := S; + Result := ''; + while SearchStr <> '' do + begin + Offset := Pos(Patt, SearchStr); + if Offset = 0 then + begin + Result := Result + NewStr; + Break; + end; // done + + if (WholeWord) then + begin + if (Offset = 1) then + PrevChar := TntWideLastChar(Result) + else + PrevChar := NewStr[Offset - 1]; + + if Offset + Length(OldPattern) <= Length(NewStr) then + NextChar := NewStr[Offset + Length(OldPattern)] + else + NextChar := WideChar(#0); + + if (not IsWordSeparator(PrevChar)) + or (not IsWordSeparator(NextChar)) then + begin + Result := Result + Copy(NewStr, 1, Offset + Length(OldPattern) - 1); + NewStr := Copy(NewStr, Offset + Length(OldPattern), MaxInt); + SearchStr := Copy(SearchStr, Offset + Length(Patt), MaxInt); + continue; + end; + end; + + Result := Result + Copy(NewStr, 1, Offset - 1) + NewPattern; + NewStr := Copy(NewStr, Offset + Length(OldPattern), MaxInt); + if not (rfReplaceAll in Flags) then + begin + Result := Result + NewStr; + Break; + end; + SearchStr := Copy(SearchStr, Offset + Length(Patt), MaxInt); + end; +end; + +function TntAdjustLineBreaksLength(const S: WideString; Style: TTntTextLineBreakStyle = tlbsCRLF): Integer; +var + Source, SourceEnd: PWideChar; +begin + Source := Pointer(S); + SourceEnd := Source + Length(S); + Result := Length(S); + while Source < SourceEnd do + begin + case Source^ of + #10, WideLineSeparator: + if Style = tlbsCRLF then + Inc(Result); + #13: + if Style = tlbsCRLF then + if Source[1] = #10 then + Inc(Source) + else + Inc(Result) + else + if Source[1] = #10 then + Dec(Result); + end; + Inc(Source); + end; +end; + +function TntAdjustLineBreaks(const S: WideString; Style: TTntTextLineBreakStyle = tlbsCRLF): WideString; +var + Source, SourceEnd, Dest: PWideChar; + DestLen: Integer; +begin + Source := Pointer(S); + SourceEnd := Source + Length(S); + DestLen := TntAdjustLineBreaksLength(S, Style); + SetString(Result, nil, DestLen); + Dest := Pointer(Result); + while Source < SourceEnd do begin + case Source^ of + #10, WideLineSeparator: + begin + if Style in [tlbsCRLF, tlbsCR] then + begin + Dest^ := #13; + Inc(Dest); + end; + if Style in [tlbsCRLF, tlbsLF] then + begin + Dest^ := #10; + Inc(Dest); + end; + Inc(Source); + end; + #13: + begin + if Style in [tlbsCRLF, tlbsCR] then + begin + Dest^ := #13; + Inc(Dest); + end; + if Style in [tlbsCRLF, tlbsLF] then + begin + Dest^ := #10; + Inc(Dest); + end; + Inc(Source); + if Source^ = #10 then Inc(Source); + end; + else + Dest^ := Source^; + Inc(Dest); + Inc(Source); + end; + end; +end; + +function WideWrapText(const Line, BreakStr: WideString; const BreakChars: TSysCharSet; + MaxCol: Integer): WideString; + + function WideCharIn(C: WideChar; SysCharSet: TSysCharSet): Boolean; + begin + Result := (C <= High(AnsiChar)) and (AnsiChar(C) in SysCharSet); + end; + +const + QuoteChars = ['''', '"']; +var + Col, Pos: Integer; + LinePos, LineLen: Integer; + BreakLen, BreakPos: Integer; + QuoteChar, CurChar: WideChar; + ExistingBreak: Boolean; +begin + Col := 1; + Pos := 1; + LinePos := 1; + BreakPos := 0; + QuoteChar := ' '; + ExistingBreak := False; + LineLen := Length(Line); + BreakLen := Length(BreakStr); + Result := ''; + while Pos <= LineLen do + begin + CurChar := Line[Pos]; + if CurChar = BreakStr[1] then + begin + if QuoteChar = ' ' then + begin + ExistingBreak := WideSameText(BreakStr, Copy(Line, Pos, BreakLen)); + if ExistingBreak then + begin + Inc(Pos, BreakLen-1); + BreakPos := Pos; + end; + end + end + else if WideCharIn(CurChar, BreakChars) then + begin + if QuoteChar = ' ' then BreakPos := Pos + end + else if WideCharIn(CurChar, QuoteChars) then + begin + if CurChar = QuoteChar then + QuoteChar := ' ' + else if QuoteChar = ' ' then + QuoteChar := CurChar; + end; + Inc(Pos); + Inc(Col); + if not (WideCharIn(QuoteChar, QuoteChars)) and (ExistingBreak or + ((Col > MaxCol) and (BreakPos > LinePos))) then + begin + Col := Pos - BreakPos; + Result := Result + Copy(Line, LinePos, BreakPos - LinePos + 1); + if not (WideCharIn(CurChar, QuoteChars)) then + while Pos <= LineLen do + begin + if WideCharIn(Line[Pos], BreakChars) then + Inc(Pos) + else if Copy(Line, Pos, Length(sLineBreak)) = sLineBreak then + Inc(Pos, Length(sLineBreak)) + else + break; + end; + if not ExistingBreak and (Pos < LineLen) then + Result := Result + BreakStr; + Inc(BreakPos); + LinePos := BreakPos; + ExistingBreak := False; + end; + end; + Result := Result + Copy(Line, LinePos, MaxInt); +end; + +function WideWrapText(const Line: WideString; MaxCol: Integer): WideString; +begin + Result := WideWrapText(Line, sLineBreak, [' ', '-', #9], MaxCol); { do not localize } +end; + +function WideIncludeTrailingBackslash(const S: WideString): WideString; +begin + Result := WideIncludeTrailingPathDelimiter(S); +end; + +function WideIncludeTrailingPathDelimiter(const S: WideString): WideString; +begin + Result := S; + if not WideIsPathDelimiter(Result, Length(Result)) then Result := Result + PathDelim; +end; + +function WideExcludeTrailingBackslash(const S: WideString): WideString; +begin + Result := WideExcludeTrailingPathDelimiter(S); +end; + +function WideExcludeTrailingPathDelimiter(const S: WideString): WideString; +begin + Result := S; + if WideIsPathDelimiter(Result, Length(Result)) then + SetLength(Result, Length(Result)-1); +end; + +function WideIsDelimiter(const Delimiters, S: WideString; Index: Integer): Boolean; +begin + Result := False; + if (Index <= 0) or (Index > Length(S)) then exit; + Result := WStrScan(PWideChar(Delimiters), S[Index]) <> nil; +end; + +function WideIsPathDelimiter(const S: WideString; Index: Integer): Boolean; +begin + Result := (Index > 0) and (Index <= Length(S)) and (S[Index] = PathDelim); +end; + +function WideLastDelimiter(const Delimiters, S: WideString): Integer; +var + P: PWideChar; +begin + Result := Length(S); + P := PWideChar(Delimiters); + while Result > 0 do + begin + if (S[Result] <> #0) and (WStrScan(P, S[Result]) <> nil) then + Exit; + Dec(Result); + end; +end; + +function WideChangeFileExt(const FileName, Extension: WideString): WideString; +var + I: Integer; +begin + I := WideLastDelimiter('.\:',Filename); + if (I = 0) or (FileName[I] <> '.') then I := MaxInt; + Result := Copy(FileName, 1, I - 1) + Extension; +end; + +function WideExtractFilePath(const FileName: WideString): WideString; +var + I: Integer; +begin + I := WideLastDelimiter('\:', FileName); + Result := Copy(FileName, 1, I); +end; + +function WideExtractFileDir(const FileName: WideString): WideString; +var + I: Integer; +begin + I := WideLastDelimiter(DriveDelim + PathDelim,Filename); + if (I > 1) and (FileName[I] = PathDelim) and + (not (FileName[I - 1] in [WideChar(PathDelim), WideChar(DriveDelim)])) then Dec(I); + Result := Copy(FileName, 1, I); +end; + +function WideExtractFileDrive(const FileName: WideString): WideString; +var + I, J: Integer; +begin + if (Length(FileName) >= 2) and (FileName[2] = DriveDelim) then + Result := Copy(FileName, 1, 2) + else if (Length(FileName) >= 2) and (FileName[1] = PathDelim) and + (FileName[2] = PathDelim) then + begin + J := 0; + I := 3; + While (I < Length(FileName)) and (J < 2) do + begin + if FileName[I] = PathDelim then Inc(J); + if J < 2 then Inc(I); + end; + if FileName[I] = PathDelim then Dec(I); + Result := Copy(FileName, 1, I); + end else Result := ''; +end; + +function WideExtractFileName(const FileName: WideString): WideString; +var + I: Integer; +begin + I := WideLastDelimiter('\:', FileName); + Result := Copy(FileName, I + 1, MaxInt); +end; + +function WideExtractFileExt(const FileName: WideString): WideString; +var + I: Integer; +begin + I := WideLastDelimiter('.\:', FileName); + if (I > 0) and (FileName[I] = '.') then + Result := Copy(FileName, I, MaxInt) else + Result := ''; +end; + +function WideExtractRelativePath(const BaseName, DestName: WideString): WideString; +var + BasePath, DestPath: WideString; + BaseLead, DestLead: PWideChar; + BasePtr, DestPtr: PWideChar; + + function WideExtractFilePathNoDrive(const FileName: WideString): WideString; + begin + Result := WideExtractFilePath(FileName); + Delete(Result, 1, Length(WideExtractFileDrive(FileName))); + end; + + function Next(var Lead: PWideChar): PWideChar; + begin + Result := Lead; + if Result = nil then Exit; + Lead := WStrScan(Lead, PathDelim); + if Lead <> nil then + begin + Lead^ := #0; + Inc(Lead); + end; + end; + +begin + if WideSameText(WideExtractFileDrive(BaseName), WideExtractFileDrive(DestName)) then + begin + BasePath := WideExtractFilePathNoDrive(BaseName); + DestPath := WideExtractFilePathNoDrive(DestName); + BaseLead := Pointer(BasePath); + BasePtr := Next(BaseLead); + DestLead := Pointer(DestPath); + DestPtr := Next(DestLead); + while (BasePtr <> nil) and (DestPtr <> nil) and WideSameText(BasePtr, DestPtr) do + begin + BasePtr := Next(BaseLead); + DestPtr := Next(DestLead); + end; + Result := ''; + while BaseLead <> nil do + begin + Result := Result + '..' + PathDelim; { Do not localize } + Next(BaseLead); + end; + if (DestPtr <> nil) and (DestPtr^ <> #0) then + Result := Result + DestPtr + PathDelim; + if DestLead <> nil then + Result := Result + DestLead; // destlead already has a trailing backslash + Result := Result + WideExtractFileName(DestName); + end + else + Result := DestName; +end; + +function WideExpandFileName(const FileName: WideString): WideString; +var + FName: PWideChar; + Buffer: array[0..MAX_PATH - 1] of WideChar; +begin + SetString(Result, Buffer, Tnt_GetFullPathNameW(PWideChar(FileName), MAX_PATH, Buffer, FName)); +end; + +function WideExtractShortPathName(const FileName: WideString): WideString; +var + Buffer: array[0..MAX_PATH - 1] of WideChar; +begin + SetString(Result, Buffer, Tnt_GetShortPathNameW(PWideChar(FileName), Buffer, MAX_PATH)); +end; + +function WideFileCreate(const FileName: WideString): Integer; +begin + Result := Integer(Tnt_CreateFileW(PWideChar(FileName), GENERIC_READ or GENERIC_WRITE, + 0, nil, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0)) +end; + +function WideFileOpen(const FileName: WideString; Mode: LongWord): Integer; +const + AccessMode: array[0..2] of LongWord = ( + GENERIC_READ, + GENERIC_WRITE, + GENERIC_READ or GENERIC_WRITE); + ShareMode: array[0..4] of LongWord = ( + 0, + 0, + FILE_SHARE_READ, + FILE_SHARE_WRITE, + FILE_SHARE_READ or FILE_SHARE_WRITE); +begin + Result := Integer(Tnt_CreateFileW(PWideChar(FileName), AccessMode[Mode and 3], + ShareMode[(Mode and $F0) shr 4], nil, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, 0)); +end; + +function WideFileAge(const FileName: WideString): Integer; +var + Handle: THandle; + FindData: TWin32FindDataW; + LocalFileTime: TFileTime; +begin + Handle := Tnt_FindFirstFileW(PWideChar(FileName), FindData); + if Handle <> INVALID_HANDLE_VALUE then + begin + Windows.FindClose(Handle); + if (FindData.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) = 0 then + begin + FileTimeToLocalFileTime(FindData.ftLastWriteTime, LocalFileTime); + if FileTimeToDosDateTime(LocalFileTime, LongRec(Result).Hi, LongRec(Result).Lo) then + Exit + end; + end; + Result := -1; +end; + +function WideFileAge(const FileName: WideString; out FileDateTime: TDateTime): Boolean; +var + Handle: THandle; + FindData: TWin32FindDataW; + LSystemTime: TSystemTime; + LocalFileTime: TFileTime; +begin + Result := False; + Handle := Tnt_FindFirstFileW(PWideChar(FileName), FindData); + if Handle <> INVALID_HANDLE_VALUE then + begin + Windows.FindClose(Handle); + if (FindData.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) = 0 then + begin + Result := True; + FileTimeToLocalFileTime(FindData.ftLastWriteTime, LocalFileTime); + FileTimeToSystemTime(LocalFileTime, LSystemTime); + with LSystemTime do + FileDateTime := EncodeDate(wYear, wMonth, wDay) + + EncodeTime(wHour, wMinute, wSecond, wMilliSeconds); + end; + end; +end; + +function WideDirectoryExists(const Name: WideString): Boolean; +var + Code: Cardinal; +begin + Code := WideFileGetAttr(Name); + Result := (Code <> INVALID_FILE_ATTRIBUTES) and ((FILE_ATTRIBUTE_DIRECTORY and Code) <> 0); +end; + +function WideFileExists(const Name: WideString): Boolean; +var + Code: Cardinal; +begin + Code := WideFileGetAttr(Name); + Result := (Code <> INVALID_FILE_ATTRIBUTES) and ((FILE_ATTRIBUTE_DIRECTORY and Code) = 0); +end; + +function WideFileGetAttr(const FileName: WideString): Cardinal; +begin + Result := Tnt_GetFileAttributesW(PWideChar(FileName)); +end; + +function WideFileSetAttr(const FileName: WideString; Attr: Integer): Boolean; +begin + Result := Tnt_SetFileAttributesW(PWideChar(FileName), Attr) +end; + +function WideFileIsReadOnly(const FileName: WideString): Boolean; +begin + Result := (Tnt_GetFileAttributesW(PWideChar(FileName)) and faReadOnly) <> 0; +end; + +function WideFileSetReadOnly(const FileName: WideString; ReadOnly: Boolean): Boolean; +var + Flags: Integer; +begin + Result := False; + Flags := Tnt_GetFileAttributesW(PWideChar(FileName)); + if Flags = -1 then Exit; + if ReadOnly then + Flags := Flags or faReadOnly + else + Flags := Flags and not faReadOnly; + Result := Tnt_SetFileAttributesW(PWideChar(FileName), Flags); +end; + +function WideForceDirectories(Dir: WideString): Boolean; +begin + Result := True; + if Length(Dir) = 0 then + raise ETntGeneralError.Create( + {$IFNDEF FPC} SCannotCreateDir {$ELSE} SCannotCreateEmptyDir {$ENDIF}); + Dir := WideExcludeTrailingBackslash(Dir); + if (Length(Dir) < 3) or WideDirectoryExists(Dir) + or (WideExtractFilePath(Dir) = Dir) then Exit; // avoid 'xyz:\' problem. + Result := WideForceDirectories(WideExtractFilePath(Dir)); + if Result then + Result := Tnt_CreateDirectoryW(PWideChar(Dir), nil) +end; + +function WideFileSearch(const Name, DirList: WideString): WideString; +var + I, P, L: Integer; + C: WideChar; +begin + Result := Name; + P := 1; + L := Length(DirList); + while True do + begin + if WideFileExists(Result) then Exit; + while (P <= L) and (DirList[P] = PathSep) do Inc(P); + if P > L then Break; + I := P; + while (P <= L) and (DirList[P] <> PathSep) do + Inc(P); + Result := Copy(DirList, I, P - I); + C := TntWideLastChar(Result); + if (C <> DriveDelim) and (C <> PathDelim) then + Result := Result + PathDelim; + Result := Result + Name; + end; + Result := ''; +end; + +function WideRenameFile(const OldName, NewName: WideString): Boolean; +begin + Result := Tnt_MoveFileW(PWideChar(OldName), PWideChar(NewName)) +end; + +function WideDeleteFile(const FileName: WideString): Boolean; +begin + Result := Tnt_DeleteFileW(PWideChar(FileName)) +end; + +function WideCopyFile(const FromFile, ToFile: WideString; FailIfExists: Boolean): Boolean; +begin + Result := Tnt_CopyFileW(PWideChar(FromFile), PWideChar(ToFile), FailIfExists) +end; + +function _WideFindMatchingFile(var F: TSearchRecW): Integer; +var + LocalFileTime: TFileTime; +begin + with F do + begin + while FindData.dwFileAttributes and ExcludeAttr <> 0 do + if not Tnt_FindNextFileW(FindHandle, FindData) then + begin + Result := GetLastError; + Exit; + end; + FileTimeToLocalFileTime(FindData.ftLastWriteTime, LocalFileTime); + FileTimeToDosDateTime(LocalFileTime, LongRec(Time).Hi, LongRec(Time).Lo); + Size := (Int64(FindData.nFileSizeHigh) shl 32) + FindData.nFileSizeLow; + Attr := FindData.dwFileAttributes; + Name := FindData.cFileName; + end; + Result := 0; +end; + +function WideFindFirst(const Path: WideString; Attr: Integer; var F: TSearchRecW): Integer; +const + faSpecial = faHidden or faSysFile {$IFNDEF COMPILER_9_UP} or faVolumeID {$ENDIF} or faDirectory; +begin + F.ExcludeAttr := not Attr and faSpecial; + F.FindHandle := Tnt_FindFirstFileW(PWideChar(Path), F.FindData); + if F.FindHandle <> INVALID_HANDLE_VALUE then + begin + Result := _WideFindMatchingFile(F); + if Result <> 0 then WideFindClose(F); + end else + Result := GetLastError; +end; + +function WideFindNext(var F: TSearchRecW): Integer; +begin + if Tnt_FindNextFileW(F.FindHandle, F.FindData) then + Result := _WideFindMatchingFile(F) else + Result := GetLastError; +end; + +procedure WideFindClose(var F: TSearchRecW); +begin + if F.FindHandle <> INVALID_HANDLE_VALUE then + begin + Windows.FindClose(F.FindHandle); + F.FindHandle := INVALID_HANDLE_VALUE; + end; +end; + +function WideCreateDir(const Dir: WideString): Boolean; +begin + Result := Tnt_CreateDirectoryW(PWideChar(Dir), nil); +end; + +function WideRemoveDir(const Dir: WideString): Boolean; +begin + Result := Tnt_RemoveDirectoryW(PWideChar(Dir)); +end; + +function WideGetCurrentDir: WideString; +begin + SetLength(Result, MAX_PATH); + Tnt_GetCurrentDirectoryW(MAX_PATH, PWideChar(Result)); + Result := PWideChar(Result); +end; + +function WideSetCurrentDir(const Dir: WideString): Boolean; +begin + Result := Tnt_SetCurrentDirectoryW(PWideChar(Dir)); +end; + +//============================================================================================= +//== DATE/TIME STRING PARSING ================================================================ +//============================================================================================= + +{$IFDEF FPC} +const + VAR_TIMEVALUEONLY = 1; + VAR_DATEVALUEONLY = 2; +{$ENDIF} + +function _IntTryStrToDateTime(Str: WideString; Flags: Integer; out DateTime: TDateTime): HResult; +begin + Result := VarDateFromStr( + {$IFDEF FPC} POLECHAR(Str) {$ELSE} Str {$ENDIF}, + GetThreadLocale, Flags, Double(DateTime)); + if (not Succeeded(Result)) then begin + if (Flags = VAR_TIMEVALUEONLY) + and SysUtils.TryStrToTime{TNT-ALLOW TryStrToTime}(Str, DateTime) then + Result := S_OK // SysUtils seems confident (works for date = "dd.MM.yy" and time = "H.mm.ss") + else if (Flags = VAR_DATEVALUEONLY) + and SysUtils.TryStrToDate{TNT-ALLOW TryStrToDate}(Str, DateTime) then + Result := S_OK // SysUtils seems confident + else if (Flags = 0) + and SysUtils.TryStrToDateTime{TNT-ALLOW TryStrToDateTime}(Str, DateTime) then + Result := S_OK // SysUtils seems confident + end; +end; + +function TntTryStrToDateTime(Str: WideString; out DateTime: TDateTime): Boolean; +begin + Result := Succeeded(_IntTryStrToDateTime(Str, 0, DateTime)); +end; + +function TntTryStrToDate(Str: WideString; out DateTime: TDateTime): Boolean; +begin + Result := Succeeded(_IntTryStrToDateTime(Str, VAR_DATEVALUEONLY, DateTime)); +end; + +function TntTryStrToTime(Str: WideString; out DateTime: TDateTime): Boolean; +begin + Result := Succeeded(_IntTryStrToDateTime(Str, VAR_TIMEVALUEONLY, DateTime)); +end; + +function ValidDateTimeStr(Str: WideString): Boolean; +var + Temp: TDateTime; +begin + Result := Succeeded(_IntTryStrToDateTime(Str, 0, Temp)); +end; + +function ValidDateStr(Str: WideString): Boolean; +var + Temp: TDateTime; +begin + Result := Succeeded(_IntTryStrToDateTime(Str, VAR_DATEVALUEONLY, Temp)); +end; + +function ValidTimeStr(Str: WideString): Boolean; +var + Temp: TDateTime; +begin + Result := Succeeded(_IntTryStrToDateTime(Str, VAR_TIMEVALUEONLY, Temp)); +end; + +function TntStrToDateTimeDef(Str: WideString; Default: TDateTime): TDateTime; +begin + if not TntTryStrToDateTime(Str, Result) then + Result := Default; +end; + +function TntStrToDateDef(Str: WideString; Default: TDateTime): TDateTime; +begin + if not TntTryStrToDate(Str, Result) then + Result := Default; +end; + +function TntStrToTimeDef(Str: WideString; Default: TDateTime): TDateTime; +begin + if not TntTryStrToTime(Str, Result) then + Result := Default; +end; + +function _IntStrToDateTime(Str: WideString; Flags: Integer; ErrorFormatStr: WideString): TDateTime; +begin + try + OleCheck(_IntTryStrToDateTime(Str, Flags, Result)); + except + on E: Exception do begin + E.Message := E.Message + CRLF + WideFormat(ErrorFormatStr, [Str]); + raise EConvertError.Create(E.Message); + end; + end; +end; + +function TntStrToDateTime(Str: WideString): TDateTime; +begin + Result := _IntStrToDateTime(Str, 0, SInvalidDateTime); +end; + +function TntStrToDate(Str: WideString): TDateTime; +begin + Result := _IntStrToDateTime(Str, VAR_DATEVALUEONLY, + {$IFNDEF FPC} SInvalidDate {$ELSE} SInvalidDateTime {$ENDIF}); +end; + +function TntStrToTime(Str: WideString): TDateTime; +begin + Result := _IntStrToDateTime(Str, VAR_TIMEVALUEONLY, + {$IFNDEF FPC} SInvalidTime {$ELSE} SInvalidDateTime {$ENDIF}); +end; + +//============================================================================================= +//== CURRENCY STRING PARSING ================================================================= +//============================================================================================= + +function TntCurrToStr(Value: Currency; lpFormat: PCurrencyFmtW = nil): WideString; +const + MAX_BUFF_SIZE = 64; // can a currency string actually be larger? +var + ValueStr: WideString; +begin + // format lpValue using ENG-US settings + ValueStr := ENG_US_FloatToStr(Value); + // get currency format + SetLength(Result, MAX_BUFF_SIZE); + if 0 = Tnt_GetCurrencyFormatW(GetThreadLocale, 0, PWideChar(ValueStr), + lpFormat, PWideChar(Result), Length(Result)) + then begin + RaiseLastOSError; + end; + Result := PWideChar(Result); +end; + +function TntStrToCurr(const S: WideString): Currency; +begin + try + OleCheck(VarCyFromStr( + {$IFDEF FPC} POLECHAR(S) {$ELSE} S {$ENDIF}, + GetThreadLocale, 0, Result)); + except + on E: Exception do begin + E.Message := E.Message + CRLF + WideFormat(SInvalidCurrency, [S]); + raise EConvertError.Create(E.Message); + end; + end; +end; + +function ValidCurrencyStr(const S: WideString): Boolean; +var + Dummy: Currency; +begin + Result := Succeeded(VarCyFromStr( + {$IFDEF FPC} POLECHAR(S) {$ELSE} S {$ENDIF}, + GetThreadLocale, 0, Dummy)); +end; + +function TntStrToCurrDef(const S: WideString; const Default: Currency): Currency; +begin + if not Succeeded(VarCyFromStr( + {$IFDEF FPC} POLECHAR(S) {$ELSE} S {$ENDIF}, + GetThreadLocale, 0, Result)) then + Result := Default; +end; + +threadvar + Currency_DecimalSep: WideString; + Currency_ThousandSep: WideString; + Currency_CurrencySymbol: WideString; + +function GetDefaultCurrencyFmt: TCurrencyFmtW; +begin + ZeroMemory(@Result, SizeOf(Result)); + Result.NumDigits := StrToIntDef(WideGetLocaleStr(GetThreadLocale, LOCALE_ICURRDIGITS, '2'), 2); + Result.LeadingZero := StrToIntDef(WideGetLocaleStr(GetThreadLocale, LOCALE_ILZERO, '1'), 1); + Result.Grouping := StrToIntDef(Copy(WideGetLocaleStr(GetThreadLocale, LOCALE_SMONGROUPING, '3;0'), 1, 1), 3); + Currency_DecimalSep := WideGetLocaleStr(GetThreadLocale, LOCALE_SMONDECIMALSEP, '.'); + Result.lpDecimalSep := {$IFNDEF FPC} PWideChar(Currency_DecimalSep) + {$ELSE} LPTSTR(PWideChar(Currency_DecimalSep)) {$ENDIF}; + Currency_ThousandSep := WideGetLocaleStr(GetThreadLocale, LOCALE_SMONTHOUSANDSEP, ','); + Result.lpThousandSep := {$IFNDEF FPC} PWideChar(Currency_ThousandSep) + {$ELSE} LPTSTR(PWideChar(Currency_ThousandSep)) {$ENDIF}; + Result.NegativeOrder := StrToIntDef(WideGetLocaleStr(GetThreadLocale, LOCALE_INEGCURR, '0'), 0); + Result.PositiveOrder := StrToIntDef(WideGetLocaleStr(GetThreadLocale, LOCALE_ICURRENCY, '0'), 0); + Currency_CurrencySymbol := WideGetLocaleStr(GetThreadLocale, LOCALE_SCURRENCY, ''); + Result.lpCurrencySymbol := {$IFNDEF FPC} PWideChar(Currency_CurrencySymbol) + {$ELSE} LPTSTR(PWideChar(Currency_CurrencySymbol)) {$ENDIF}; +end; + +//============================================================================================= + +{$IFDEF FPC} +function GetLocaleStr(Locale, LocaleType: Integer; const Default: string): string; +var + L: Integer; + Buffer: array[0..255] of Char; +begin + L := GetLocaleInfo(Locale, LocaleType, Buffer, SizeOf(Buffer)); + if L > 0 then SetString(Result, Buffer, L - 1) else Result := Default; +end; +{$ENDIF} + +function WideGetLocaleStr(LocaleID: LCID; LocaleType: Integer; const Default: WideString): WideString; +var + L: Integer; +begin + if (not Win32PlatformIsUnicode) then + Result := GetLocaleStr{TNT-ALLOW GetLocaleStr}(LocaleID, LocaleType, Default) + else begin + SetLength(Result, 255); + L := GetLocaleInfoW(LocaleID, LocaleType, PWideChar(Result), Length(Result)); + if L > 0 then + SetLength(Result, L - 1) + else + Result := Default; + end; +end; + +function WideSysErrorMessage(ErrorCode: Integer): WideString; +begin + Result := WideLibraryErrorMessage('system', 0, ErrorCode); +end; + +function WideLibraryErrorMessage(const LibName: WideString; Dll: THandle; ErrorCode: Integer): WideString; +var + Len: Integer; + AnsiResult: AnsiString; + Flags: Cardinal; +begin + Flags := FORMAT_MESSAGE_FROM_SYSTEM or FORMAT_MESSAGE_IGNORE_INSERTS or FORMAT_MESSAGE_ARGUMENT_ARRAY; + if Dll <> 0 then + Flags := Flags or FORMAT_MESSAGE_FROM_HMODULE; + if Win32PlatformIsUnicode then begin + SetLength(Result, 256); + Len := FormatMessageW(Flags, Pointer(Dll), ErrorCode, 0, PWideChar(Result), Length(Result), nil); + SetLength(Result, Len); + end else begin + SetLength(AnsiResult, 256); + Len := FormatMessageA(Flags, Pointer(Dll), ErrorCode, 0, PAnsiChar(AnsiResult), Length(AnsiResult), nil); + SetLength(AnsiResult, Len); + Result := AnsiResult; + end; + if Trim(Result) = '' then + Result := WideFormat('Unspecified error (%d) from %s.', [ErrorCode, LibName]); +end; + +{$IFNDEF COMPILER_7_UP} +function CheckWin32Version(AMajor: Integer; AMinor: Integer = 0): Boolean; +begin + Result := (Win32MajorVersion > AMajor) or + ((Win32MajorVersion = AMajor) and + (Win32MinorVersion >= AMinor)); +end; +{$ENDIF} + +function WinCheckH(RetVal: Cardinal): Cardinal; +begin + if RetVal = 0 then RaiseLastOSError; + Result := RetVal; +end; + +function WinCheckFileH(RetVal: Cardinal): Cardinal; +begin + if RetVal = INVALID_HANDLE_VALUE then RaiseLastOSError; + Result := RetVal; +end; + +function WinCheckP(RetVal: Pointer): Pointer; +begin + if RetVal = nil then RaiseLastOSError; + Result := RetVal; +end; + +function WideGetModuleFileName(Instance: HModule): WideString; +begin + SetLength(Result, MAX_PATH); + WinCheckH(Tnt_GetModuleFileNameW(Instance, PWideChar(Result), Length(Result))); + Result := PWideChar(Result) +end; + +function WideSafeLoadLibrary(const Filename: Widestring; ErrorMode: UINT): HMODULE; +var + OldMode: UINT; + FPUControlWord: Word; +begin + OldMode := SetErrorMode(ErrorMode); + try + asm + FNSTCW FPUControlWord + end; + try + Result := Tnt_LoadLibraryW(PWideChar(Filename)); + finally + asm + FNCLEX + FLDCW FPUControlWord + end; + end; + finally + SetErrorMode(OldMode); + end; +end; + +{$IFNDEF FPC} +function WideLoadPackage(const Name: Widestring): HMODULE; +begin + Result := WideSafeLoadLibrary(Name); + if Result = 0 then + begin + raise EPackageError.CreateFmt(sErrorLoadingPackage, [Name, WideSysErrorMessage(GetLastError)]); + end; + try + InitializePackage(Result); + except + FreeLibrary(Result); + raise; + end; +end; +{$ENDIF} + +function _WideCharType(WC: WideChar; dwInfoType: Cardinal): Word; +begin + Win32Check(Tnt_GetStringTypeExW(GetThreadLocale, dwInfoType, PWideChar(@WC), 1, Result)) +end; + +function IsWideCharUpper(WC: WideChar): Boolean; +begin + Result := (_WideCharType(WC, CT_CTYPE1) and C1_UPPER) <> 0; +end; + +function IsWideCharLower(WC: WideChar): Boolean; +begin + Result := (_WideCharType(WC, CT_CTYPE1) and C1_LOWER) <> 0; +end; + +function IsWideCharDigit(WC: WideChar): Boolean; +begin + Result := (_WideCharType(WC, CT_CTYPE1) and C1_DIGIT) <> 0; +end; + +function IsWideCharSpace(WC: WideChar): Boolean; +begin + Result := (_WideCharType(WC, CT_CTYPE1) and C1_SPACE) <> 0; +end; + +function IsWideCharPunct(WC: WideChar): Boolean; +begin + Result := (_WideCharType(WC, CT_CTYPE1) and C1_PUNCT) <> 0; +end; + +function IsWideCharCntrl(WC: WideChar): Boolean; +begin + Result := (_WideCharType(WC, CT_CTYPE1) and C1_CNTRL) <> 0; +end; + +function IsWideCharBlank(WC: WideChar): Boolean; +begin + Result := (_WideCharType(WC, CT_CTYPE1) and C1_BLANK) <> 0; +end; + +function IsWideCharXDigit(WC: WideChar): Boolean; +begin + Result := (_WideCharType(WC, CT_CTYPE1) and C1_XDIGIT) <> 0; +end; + +function IsWideCharAlpha(WC: WideChar): Boolean; +begin + Result := (_WideCharType(WC, CT_CTYPE1) and C1_ALPHA) <> 0; +end; + +function IsWideCharAlphaNumeric(WC: WideChar): Boolean; +begin + Result := (_WideCharType(WC, CT_CTYPE1) and (C1_ALPHA + C1_DIGIT)) <> 0; +end; + +function WideTextPos(const SubStr, S: WideString): Integer; +begin + Result := Pos(Tnt_WideUpperCase(SubStr), Tnt_WideUpperCase(S)); +end; + +function FindDoubleTerminator(P: PWideChar): PWideChar; +begin + Result := P; + while True do begin + Result := WStrScan(Result, #0); + Inc(Result); + if Result^ = #0 then begin + Dec(Result); + break; + end; + end; +end; + +function ExtractStringArrayStr(P: PWideChar): WideString; +var + PEnd: PWideChar; +begin + PEnd := FindDoubleTerminator(P); + Inc(PEnd, 2); // move past #0#0 + SetString(Result, P, PEnd - P); +end; + +function ExtractStringFromStringArray(var P: PWideChar; Separator: WideChar = #0): WideString; +var + Start: PWideChar; +begin + Start := P; + P := WStrScan(Start, Separator); + if P = nil then begin + Result := Start; + P := WStrEnd(Start); + end else begin + SetString(Result, Start, P - Start); + Inc(P); + end; +end; + +function ExtractStringsFromStringArray(P: PWideChar; Separator: WideChar = #0): TWideStringDynArray; +const + GROW_COUNT = 256; +var + Count: Integer; + Item: WideString; +begin + Count := 0; + SetLength(Result, GROW_COUNT); + Item := ExtractStringFromStringArray(P, Separator); + While Item <> '' do begin + if Count > High(Result) then + SetLength(Result, Length(Result) + GROW_COUNT); + Result[Count] := Item; + Inc(Count); + Item := ExtractStringFromStringArray(P, Separator); + end; + SetLength(Result, Count); +end; + +function IsWideCharMappableToAnsi(const WC: WideChar): Boolean; +var + UsedDefaultChar: BOOL; +begin + WideCharToMultiByte(DefaultSystemCodePage, 0, PWideChar(@WC), 1, nil, 0, nil, @UsedDefaultChar); + Result := not UsedDefaultChar; +end; + +function IsWideStringMappableToAnsi(const WS: WideString): Boolean; +var + UsedDefaultChar: BOOL; +begin + WideCharToMultiByte(DefaultSystemCodePage, 0, PWideChar(WS), Length(WS), nil, 0, nil, @UsedDefaultChar); + Result := not UsedDefaultChar; +end; + +function IsRTF(const Value: WideString): Boolean; +const + RTF_BEGIN_1 = WideString('{\RTF'); + RTF_BEGIN_2 = WideString('{URTF'); +begin + Result := (WideTextPos(RTF_BEGIN_1, Value) = 1) + or (WideTextPos(RTF_BEGIN_2, Value) = 1); +end; + +{$IFDEF COMPILER_7_UP} +var + Cached_ENG_US_FormatSettings: TFormatSettings; + Cached_ENG_US_FormatSettings_Time: Cardinal; + +function ENG_US_FormatSettings: TFormatSettings; +begin + if Cached_ENG_US_FormatSettings_Time = _SettingChangeTime then + Result := Cached_ENG_US_FormatSettings + else begin + GetLocaleFormatSettings(MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US)), Result); + Result.DecimalSeparator := '.'; // ignore overrides + Cached_ENG_US_FormatSettings := Result; + Cached_ENG_US_FormatSettings_Time := _SettingChangeTime; + end; + end; + +function ENG_US_FloatToStr(Value: Extended): WideString; +begin + Result := FloatToStr(Value, ENG_US_FormatSettings); +end; + +function ENG_US_StrToFloat(const S: WideString): Extended; +begin + if not TextToFloat(PAnsiChar(AnsiString(S)), Result, fvExtended, ENG_US_FormatSettings) then + Result := StrToFloat(S); // try using native format +end; + +{$ELSE} + +function ENG_US_FloatToStr(Value: Extended): WideString; +var + SaveDecimalSep: AnsiChar; +begin + SaveDecimalSep := SysUtils.DecimalSeparator; + try + SysUtils.DecimalSeparator := '.'; + Result := FloatToStr(Value); + finally + SysUtils.DecimalSeparator := SaveDecimalSep; + end; +end; + +function ENG_US_StrToFloat(const S: WideString): Extended; +var + SaveDecimalSep: AnsiChar; +begin + try + SaveDecimalSep := SysUtils.DecimalSeparator; + try + SysUtils.DecimalSeparator := '.'; + Result := StrToFloat(S); + finally + SysUtils.DecimalSeparator := SaveDecimalSep; + end; + except + if SysUtils.DecimalSeparator <> '.' then + Result := StrToFloat(S) // try using native format + else + raise; + end; +end; +{$ENDIF} + +//--------------------------------------------------------------------------------------------- +// Tnt - Variants +//--------------------------------------------------------------------------------------------- + +initialization + Win32PlatformIsUnicode := (Win32Platform = VER_PLATFORM_WIN32_NT); + Win32PlatformIsXP := ((Win32MajorVersion = 5) and (Win32MinorVersion >= 1)) + or (Win32MajorVersion > 5); + Win32PlatformIs2003 := ((Win32MajorVersion = 5) and (Win32MinorVersion >= 2)) + or (Win32MajorVersion > 5); + Win32PlatformIsVista := (Win32MajorVersion >= 6); + +finalization + Currency_DecimalSep := ''; {make memory sleuth happy} + Currency_ThousandSep := ''; {make memory sleuth happy} + Currency_CurrencySymbol := ''; {make memory sleuth happy} + +end. diff --git a/Lua/src/lib/TntUnicodeControls/TntSystem.pas b/Lua/src/lib/TntUnicodeControls/TntSystem.pas new file mode 100644 index 00000000..e613ce0c --- /dev/null +++ b/Lua/src/lib/TntUnicodeControls/TntSystem.pas @@ -0,0 +1,1427 @@ + +{*****************************************************************************} +{ } +{ Tnt Delphi Unicode Controls } +{ http://www.tntware.com/delphicontrols/unicode/ } +{ Version: 2.3.0 } +{ } +{ Copyright (c) 2002-2007, Troy Wolbrink (troy.wolbrink@tntware.com) } +{ } +{*****************************************************************************} + +unit TntSystem; + +{$IFDEF FPC} + {$MODE Delphi} +{$ENDIF} + +{$INCLUDE TntCompilers.inc} + +{*****************************************************************************} +{ Special thanks go to Francisco Leong for originating the design for } +{ WideString-enabled resourcestrings. } +{*****************************************************************************} + +interface + +uses + Windows; + +// These functions should not be used by Delphi code since conversions are implicit. +{TNT-WARN WideCharToString} +{TNT-WARN WideCharLenToString} +{TNT-WARN WideCharToStrVar} +{TNT-WARN WideCharLenToStrVar} +{TNT-WARN StringToWideChar} + +// ................ ANSI TYPES ................ +{TNT-WARN Char} +{TNT-WARN PChar} +{TNT-WARN String} + +{TNT-WARN CP_ACP} // <-- use DefaultSystemCodePage +function DefaultSystemCodePage: Cardinal; // implicitly used when converting AnsiString <--> WideString. + +{$IFNDEF FPC} +var + WideCustomLoadResString: function(ResStringRec: PResStringRec; var Value: WideString): Boolean; +{$ENDIF} + +{TNT-WARN LoadResString} +function WideLoadResString(ResStringRec: PResStringRec): WideString; +{TNT-WARN ParamCount} +function WideParamCount: Integer; +{TNT-WARN ParamStr} +function WideParamStr(Index: Integer): WideString; + +// ......... introduced ......... + +const + { Each Unicode stream should begin with the code U+FEFF, } + { which the standard defines as the *byte order mark*. } + UNICODE_BOM = WideChar($FEFF); + UNICODE_BOM_SWAPPED = WideChar($FFFE); + UTF8_BOM = AnsiString(#$EF#$BB#$BF); + +function WideStringToUTF8(const S: WideString): AnsiString; +function UTF8ToWideString(const S: AnsiString): WideString; + +function WideStringToUTF7(const W: WideString): AnsiString; +function UTF7ToWideString(const S: AnsiString): WideString; + +function StringToWideStringEx(const S: AnsiString; CodePage: Cardinal): WideString; +function WideStringToStringEx(const WS: WideString; CodePage: Cardinal): AnsiString; + +function UCS2ToWideString(const Value: AnsiString): WideString; +function WideStringToUCS2(const Value: WideString): AnsiString; + +function CharSetToCodePage(ciCharset: UINT): Cardinal; +function LCIDToCodePage(ALcid: LCID): Cardinal; +function KeyboardCodePage: Cardinal; +function KeyUnicode(CharCode: Word): WideChar; + +procedure StrSwapByteOrder(Str: PWideChar); + +{$IFDEF USE_SYSTEM_OVERRIDES} + +type + TTntSystemUpdate = + (tsWideResourceStrings + {$IFNDEF COMPILER_9_UP}, tsFixImplicitCodePage, tsFixWideStrConcat, tsFixWideFormat {$ENDIF} + ); + TTntSystemUpdateSet = set of TTntSystemUpdate; + +const + AllTntSystemUpdates = [Low(TTntSystemUpdate)..High(TTntSystemUpdate)]; + +procedure InstallTntSystemUpdates(Updates: TTntSystemUpdateSet = AllTntSystemUpdates); + +{$ENDIF USE_SYSTEM_OVERRIDES} + +implementation + +uses + SysUtils, Variants, TntWindows, TntSysUtils; + +var + GDefaultSystemCodePage: Cardinal; + +function DefaultSystemCodePage: Cardinal; +begin + Result := GDefaultSystemCodePage; +end; + +{$IFDEF USE_SYSTEM_OVERRIDES} +var + IsDebugging: Boolean; +{$ENDIF USE_SYSTEM_OVERRIDES} + +function WideLoadResStringDetect(ResStringRec: PResStringRec): WideString; +var + PCustom: PAnsiChar; +begin + // custom string pointer + PCustom := PAnsiChar(ResStringRec); { I would like to use PWideChar, but this would break legacy code. } + if (StrLen{TNT-ALLOW StrLen}(PCustom) > Cardinal(Length(UTF8_BOM))) + and CompareMem(PCustom, PAnsiChar(UTF8_BOM), Length(UTF8_BOM)) then + // detected UTF8 + Result := UTF8ToWideString(PAnsiChar(PCustom + Length(UTF8_BOM))) + else + // normal + Result := PCustom; +end; + +{$IFNDEF FPC} + +function WideLoadResString(ResStringRec: PResStringRec): WideString; +const + MAX_RES_STRING_SIZE = 4097; { MSDN documents this as the maximum size of a string in table. } +var + Buffer: array [0..MAX_RES_STRING_SIZE] of WideChar; { Buffer leaves room for null terminator. } +begin + if Assigned(WideCustomLoadResString) and WideCustomLoadResString(ResStringRec, Result) then + exit; { a custom resourcestring has been loaded. } + + if ResStringRec = nil then + Result := '' + else if ResStringRec.Identifier < 64*1024 then + SetString(Result, Buffer, + Tnt_LoadStringW(FindResourceHInstance(ResStringRec.Module^), + ResStringRec.Identifier, Buffer, MAX_RES_STRING_SIZE)) + else begin + Result := WideLoadResStringDetect(ResStringRec); + end; +end; + +{$ELSE} + +function WideLoadResString(ResStringRec: PResStringRec): WideString; +begin + Result := WideLoadResStringDetect(ResStringRec); +end; + +{$ENDIF} + +function WideGetParamStr(P: PWideChar; var Param: WideString): PWideChar; +var + i, Len: Integer; + Start, S, Q: PWideChar; +begin + while True do + begin + while (P[0] <> #0) and (P[0] <= ' ') do + Inc(P); + if (P[0] = '"') and (P[1] = '"') then Inc(P, 2) else Break; + end; + Len := 0; + Start := P; + while P[0] > ' ' do + begin + if P[0] = '"' then + begin + Inc(P); + while (P[0] <> #0) and (P[0] <> '"') do + begin + Q := P + 1; + Inc(Len, Q - P); + P := Q; + end; + if P[0] <> #0 then + Inc(P); + end + else + begin + Q := P + 1; + Inc(Len, Q - P); + P := Q; + end; + end; + + SetLength(Param, Len); + + P := Start; + S := PWideChar(Param); + i := 0; + while P[0] > ' ' do + begin + if P[0] = '"' then + begin + Inc(P); + while (P[0] <> #0) and (P[0] <> '"') do + begin + Q := P + 1; + while P < Q do + begin + S[i] := P^; + Inc(P); + Inc(i); + end; + end; + if P[0] <> #0 then Inc(P); + end + else + begin + Q := P + 1; + while P < Q do + begin + S[i] := P^; + Inc(P); + Inc(i); + end; + end; + end; + + Result := P; +end; + +function WideParamCount: Integer; +var + P: PWideChar; + S: WideString; +begin + P := WideGetParamStr(GetCommandLineW, S); + Result := 0; + while True do + begin + P := WideGetParamStr(P, S); + if S = '' then Break; + Inc(Result); + end; +end; + +function WideParamStr(Index: Integer): WideString; +var + P: PWideChar; +begin + if Index = 0 then + Result := WideGetModuleFileName(0) + else + begin + P := GetCommandLineW; + while True do + begin + P := WideGetParamStr(P, Result); + if (Index = 0) or (Result = '') then Break; + Dec(Index); + end; + end; +end; + +function WideStringToUTF8(const S: WideString): AnsiString; +begin + Result := UTF8Encode(S); +end; + +function UTF8ToWideString(const S: AnsiString): WideString; +begin + Result := UTF8Decode(S); +end; + + { ======================================================================= } + { Original File: ConvertUTF7.c } + { Author: David B. Goldsmith } + { Copyright (C) 1994, 1996 Taligent, Inc. All rights reserved. } + { } + { This code is copyrighted. Under the copyright laws, this code may not } + { be copied, in whole or part, without prior written consent of Taligent. } + { } + { Taligent grants the right to use this code as long as this ENTIRE } + { copyright notice is reproduced in the code. The code is provided } + { AS-IS, AND TALIGENT DISCLAIMS ALL WARRANTIES, EITHER EXPRESS OR } + { IMPLIED, INCLUDING, BUT NOT LIMITED TO IMPLIED WARRANTIES OF } + { MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT } + { WILL TALIGENT BE LIABLE FOR ANY DAMAGES WHATSOEVER (INCLUDING, } + { WITHOUT LIMITATION, DAMAGES FOR LOSS OF BUSINESS PROFITS, BUSINESS } + { INTERRUPTION, LOSS OF BUSINESS INFORMATION, OR OTHER PECUNIARY } + { LOSS) ARISING OUT OF THE USE OR INABILITY TO USE THIS CODE, EVEN } + { IF TALIGENT HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. } + { BECAUSE SOME STATES DO NOT ALLOW THE EXCLUSION OR LIMITATION OF } + { LIABILITY FOR CONSEQUENTIAL OR INCIDENTAL DAMAGES, THE ABOVE } + { LIMITATION MAY NOT APPLY TO YOU. } + { } + { RESTRICTED RIGHTS LEGEND: Use, duplication, or disclosure by the } + { government is subject to restrictions as set forth in subparagraph } + { (c)(l)(ii) of the Rights in Technical Data and Computer Software } + { clause at DFARS 252.227-7013 and FAR 52.227-19. } + { } + { This code may be protected by one or more U.S. and International } + { Patents. } + { } + { TRADEMARKS: Taligent and the Taligent Design Mark are registered } + { trademarks of Taligent, Inc. } + { ======================================================================= } + +type UCS2 = Word; + +const + _base64: AnsiString = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; + _direct: AnsiString = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789''(),-./:?'; + _optional: AnsiString = '!"#$%&*;<=>@[]^_`{|}'; + _spaces: AnsiString = #9#13#10#32; + +var + base64: PAnsiChar; + invbase64: array[0..127] of SmallInt; + direct: PAnsiChar; + optional: PAnsiChar; + spaces: PAnsiChar; + mustshiftsafe: array[0..127] of AnsiChar; + mustshiftopt: array[0..127] of AnsiChar; + +var + needtables: Boolean = True; + +procedure Initialize_UTF7_Data; +begin + base64 := PAnsiChar(_base64); + direct := PAnsiChar(_direct); + optional := PAnsiChar(_optional); + spaces := PAnsiChar(_spaces); +end; + +procedure tabinit; +var + i: Integer; + limit: Integer; +begin + i := 0; + while (i < 128) do + begin + mustshiftopt[i] := #1; + mustshiftsafe[i] := #1; + invbase64[i] := -1; + Inc(i); + end { For }; + limit := Length(_Direct); + i := 0; + while (i < limit) do + begin + mustshiftopt[Integer(direct[i])] := #0; + mustshiftsafe[Integer(direct[i])] := #0; + Inc(i); + end { For }; + limit := Length(_Spaces); + i := 0; + while (i < limit) do + begin + mustshiftopt[Integer(spaces[i])] := #0; + mustshiftsafe[Integer(spaces[i])] := #0; + Inc(i); + end { For }; + limit := Length(_Optional); + i := 0; + while (i < limit) do + begin + mustshiftopt[Integer(optional[i])] := #0; + Inc(i); + end { For }; + limit := Length(_Base64); + i := 0; + while (i < limit) do + begin + invbase64[Integer(base64[i])] := i; + Inc(i); + end { For }; + needtables := False; +end; { tabinit } + +function WRITE_N_BITS(x: UCS2; n: Integer; var BITbuffer: Cardinal; var bufferbits: Integer): Integer; +begin + BITbuffer := BITbuffer or (x and (not (-1 shl n))) shl (32 - n - bufferbits); + bufferbits := bufferbits + n; + Result := bufferbits; +end; { WRITE_N_BITS } + +function READ_N_BITS(n: Integer; var BITbuffer: Cardinal; var bufferbits: Integer): UCS2; +var + buffertemp: Cardinal; +begin + buffertemp := BITbuffer shr (32 - n); + BITbuffer := BITbuffer shl n; + bufferbits := bufferbits - n; + Result := UCS2(buffertemp); +end; { READ_N_BITS } + +function ConvertUCS2toUTF7(var sourceStart: PWideChar; sourceEnd: PWideChar; + var targetStart: PAnsiChar; targetEnd: PAnsiChar; optional: Boolean; + verbose: Boolean): Integer; +var + r: UCS2; + target: PAnsiChar; + source: PWideChar; + BITbuffer: Cardinal; + bufferbits: Integer; + shifted: Boolean; + needshift: Boolean; + done: Boolean; + mustshift: PAnsiChar; +begin + Initialize_UTF7_Data; + Result := 0; + BITbuffer := 0; + bufferbits := 0; + shifted := False; + source := sourceStart; + target := targetStart; + r := 0; + if needtables then + tabinit; + if optional then + mustshift := @mustshiftopt[0] + else + mustshift := @mustshiftsafe[0]; + repeat + done := source >= sourceEnd; + if not Done then + begin + r := Word(source^); + Inc(Source); + end { If }; + needshift := (not done) and ((r > $7F) or (mustshift[r] <> #0)); + if needshift and (not shifted) then + begin + if (Target >= TargetEnd) then + begin + Result := 2; + break; + end { If }; + target^ := '+'; + Inc(target); + { Special case handling of the SHIFT_IN character } + if (r = UCS2('+')) then + begin + if (target >= targetEnd) then + begin + Result := 2; + break; + end; + target^ := '-'; + Inc(target); + end + else + shifted := True; + end { If }; + if shifted then + begin + { Either write the character to the bit buffer, or pad } + { the bit buffer out to a full base64 character. } + { } + if needshift then + WRITE_N_BITS(r, 16, BITbuffer, bufferbits) + else + WRITE_N_BITS(0, (6 - (bufferbits mod 6)) mod 6, BITbuffer, + bufferbits); + { Flush out as many full base64 characters as possible } + { from the bit buffer. } + { } + while (target < targetEnd) and (bufferbits >= 6) do + begin + Target^ := base64[READ_N_BITS(6, BITbuffer, bufferbits)]; + Inc(Target); + end { While }; + if (bufferbits >= 6) then + begin + if (target >= targetEnd) then + begin + Result := 2; + break; + end { If }; + end { If }; + if (not needshift) then + begin + { Write the explicit shift out character if } + { 1) The caller has requested we always do it, or } + { 2) The directly encoded character is in the } + { base64 set, or } + { 3) The directly encoded character is SHIFT_OUT. } + { } + if verbose or ((not done) and ((invbase64[r] >= 0) or (r = + Integer('-')))) then + begin + if (target >= targetEnd) then + begin + Result := 2; + Break; + end { If }; + Target^ := '-'; + Inc(Target); + end { If }; + shifted := False; + end { If }; + { The character can be directly encoded as ASCII. } + end { If }; + if (not needshift) and (not done) then + begin + if (target >= targetEnd) then + begin + Result := 2; + break; + end { If }; + Target^ := AnsiChar(r); + Inc(Target); + end { If }; + until (done); + sourceStart := source; + targetStart := target; +end; { ConvertUCS2toUTF7 } + +function ConvertUTF7toUCS2(var sourceStart: PAnsiChar; sourceEnd: PAnsiChar; + var targetStart: PWideChar; targetEnd: PWideChar): Integer; +var + target: PWideChar { Register }; + source: PAnsiChar { Register }; + BITbuffer: Cardinal { & "Address Of" Used }; + bufferbits: Integer { & "Address Of" Used }; + shifted: Boolean { Used In Boolean Context }; + first: Boolean { Used In Boolean Context }; + wroteone: Boolean; + base64EOF: Boolean; + base64value: Integer; + done: Boolean; + c: UCS2; + prevc: UCS2; + junk: UCS2 { Used In Boolean Context }; +begin + Initialize_UTF7_Data; + Result := 0; + BITbuffer := 0; + bufferbits := 0; + shifted := False; + first := False; + wroteone := False; + source := sourceStart; + target := targetStart; + c := 0; + if needtables then + tabinit; + repeat + { read an ASCII character c } + done := Source >= SourceEnd; + if (not done) then + begin + c := Word(Source^); + Inc(Source); + end { If }; + if shifted then + begin + { We're done with a base64 string if we hit EOF, it's not a valid } + { ASCII character, or it's not in the base64 set. } + { } + base64value := invbase64[c]; + base64EOF := (done or (c > $7F)) or (base64value < 0); + if base64EOF then + begin + shifted := False; + { If the character causing us to drop out was SHIFT_IN or } + { SHIFT_OUT, it may be a special escape for SHIFT_IN. The } + { test for SHIFT_IN is not necessary, but allows an alternate } + { form of UTF-7 where SHIFT_IN is escaped by SHIFT_IN. This } + { only works for some values of SHIFT_IN. } + { } + if ((not done) and ((c = Integer('+')) or (c = Integer('-')))) then + begin + { get another character c } + prevc := c; + Done := Source >= SourceEnd; + if (not Done) then + begin + c := Word(Source^); + Inc(Source); + { If no base64 characters were encountered, and the } + { character terminating the shift sequence was } + { SHIFT_OUT, then it's a special escape for SHIFT_IN. } + { } + end; + if first and (prevc = Integer('-')) then + begin + { write SHIFT_IN unicode } + if (target >= targetEnd) then + begin + Result := 2; + break; + end { If }; + Target^ := WideChar('+'); + Inc(Target); + end + else + begin + if (not wroteone) then + begin + Result := 1; + end { If }; + end { Else }; + ; + end { If } + else + begin + if (not wroteone) then + begin + Result := 1; + end { If }; + end { Else }; + end { If } + else + begin + { Add another 6 bits of base64 to the bit buffer. } + WRITE_N_BITS(base64value, 6, BITbuffer, + bufferbits); + first := False; + end { Else }; + { Extract as many full 16 bit characters as possible from the } + { bit buffer. } + { } + while (bufferbits >= 16) and (target < targetEnd) do + begin + { write a unicode } + Target^ := WideChar(READ_N_BITS(16, BITbuffer, bufferbits)); + Inc(Target); + wroteone := True; + end { While }; + if (bufferbits >= 16) then + begin + if (target >= targetEnd) then + begin + Result := 2; + Break; + end; + end { If }; + if (base64EOF) then + begin + junk := READ_N_BITS(bufferbits, BITbuffer, bufferbits); + if (junk <> 0) then + begin + Result := 1; + end { If }; + end { If }; + end { If }; + if (not shifted) and (not done) then + begin + if (c = Integer('+')) then + begin + shifted := True; + first := True; + wroteone := False; + end { If } + else + begin + { It must be a directly encoded character. } + if (c > $7F) then + begin + Result := 1; + end { If }; + if (target >= targetEnd) then + begin + Result := 2; + break; + end { If }; + Target^ := WideChar(c); + Inc(Target); + end { Else }; + end { If }; + until (done); + sourceStart := source; + targetStart := target; +end; { ConvertUTF7toUCS2 } + + {*****************************************************************************} + { Thanks to Francisco Leong for providing the Pascal conversion of } + { ConvertUTF7.c (by David B. Goldsmith) } + {*****************************************************************************} + +resourcestring + SBufferOverflow = 'Buffer overflow'; + SInvalidUTF7 = 'Invalid UTF7'; + +function WideStringToUTF7(const W: WideString): AnsiString; +var + SourceStart, SourceEnd: PWideChar; + TargetStart, TargetEnd: PAnsiChar; +begin + if W = '' then + Result := '' + else + begin + SetLength(Result, Length(W) * 7); // Assume worst case + SourceStart := PWideChar(@W[1]); + SourceEnd := PWideChar(@W[Length(W)]) + 1; + TargetStart := PAnsiChar(@Result[1]); + TargetEnd := PAnsiChar(@Result[Length(Result)]) + 1; + if ConvertUCS2toUTF7(SourceStart, SourceEnd, TargetStart, + TargetEnd, True, False) <> 0 + then + raise ETntInternalError.Create(SBufferOverflow); + SetLength(Result, TargetStart - PAnsiChar(@Result[1])); + end; +end; + +function UTF7ToWideString(const S: AnsiString): WideString; +var + SourceStart, SourceEnd: PAnsiChar; + TargetStart, TargetEnd: PWideChar; +begin + if (S = '') then + Result := '' + else + begin + SetLength(Result, Length(S)); // Assume Worst case + SourceStart := PAnsiChar(@S[1]); + SourceEnd := PAnsiChar(@S[Length(S)]) + 1; + TargetStart := PWideChar(@Result[1]); + TargetEnd := PWideChar(@Result[Length(Result)]) + 1; + case ConvertUTF7toUCS2(SourceStart, SourceEnd, TargetStart, + TargetEnd) of + 1: raise ETntGeneralError.Create(SInvalidUTF7); + 2: raise ETntInternalError.Create(SBufferOverflow); + end; + SetLength(Result, TargetStart - PWideChar(@Result[1])); + end; +end; + +function StringToWideStringEx(const S: AnsiString; CodePage: Cardinal): WideString; +var + InputLength, + OutputLength: Integer; +begin + if CodePage = CP_UTF7 then + Result := UTF7ToWideString(S) // CP_UTF7 not supported on Windows 95 + else if CodePage = CP_UTF8 then + Result := UTF8ToWideString(S) // CP_UTF8 not supported on Windows 95 + else begin + InputLength := Length(S); + OutputLength := MultiByteToWideChar(CodePage, 0, PAnsiChar(S), InputLength, nil, 0); + SetLength(Result, OutputLength); + MultiByteToWideChar(CodePage, 0, PAnsiChar(S), InputLength, PWideChar(Result), OutputLength); + end; +end; + +function WideStringToStringEx(const WS: WideString; CodePage: Cardinal): AnsiString; +var + InputLength, + OutputLength: Integer; +begin + if CodePage = CP_UTF7 then + Result := WideStringToUTF7(WS) // CP_UTF7 not supported on Windows 95 + else if CodePage = CP_UTF8 then + Result := WideStringToUTF8(WS) // CP_UTF8 not supported on Windows 95 + else begin + InputLength := Length(WS); + OutputLength := WideCharToMultiByte(CodePage, 0, PWideChar(WS), InputLength, nil, 0, nil, nil); + SetLength(Result, OutputLength); + WideCharToMultiByte(CodePage, 0, PWideChar(WS), InputLength, PAnsiChar(Result), OutputLength, nil, nil); + end; +end; + +function UCS2ToWideString(const Value: AnsiString): WideString; +begin + if Length(Value) = 0 then + Result := '' + else + SetString(Result, PWideChar(@Value[1]), Length(Value) div SizeOf(WideChar)) +end; + +function WideStringToUCS2(const Value: WideString): AnsiString; +begin + if Length(Value) = 0 then + Result := '' + else + SetString(Result, PAnsiChar(@Value[1]), Length(Value) * SizeOf(WideChar)) +end; + +{ Windows.pas doesn't declare TranslateCharsetInfo() correctly. } +function TranslateCharsetInfo(lpSrc: PDWORD; var lpCs: TCharsetInfo; dwFlags: DWORD): BOOL; stdcall; external gdi32 name 'TranslateCharsetInfo'; + +function CharSetToCodePage(ciCharset: UINT): Cardinal; +var + C: TCharsetInfo; +begin + Win32Check(TranslateCharsetInfo(PDWORD(ciCharset), C, TCI_SRCCHARSET)); + Result := C.ciACP +end; + +function LCIDToCodePage(ALcid: LCID): Cardinal; +var + Buf: array[0..6] of AnsiChar; +begin + GetLocaleInfo(ALcid, LOCALE_IDefaultAnsiCodePage, Buf, 6); + Result := StrToIntDef(Buf, GetACP); +end; + +function KeyboardCodePage: Cardinal; +begin + Result := LCIDToCodePage(GetKeyboardLayout(0) and $FFFF); +end; + +function KeyUnicode(CharCode: Word): WideChar; +var + AChar: AnsiChar; +begin + // converts the given character (as it comes with a WM_CHAR message) into its + // corresponding Unicode character depending on the active keyboard layout + if CharCode <= Word(High(AnsiChar)) then begin + AChar := AnsiChar(CharCode); + MultiByteToWideChar(KeyboardCodePage, MB_USEGLYPHCHARS, @AChar, 1, @Result, 1); + end else + Result := WideChar(CharCode); +end; + +procedure StrSwapByteOrder(Str: PWideChar); +var + P: PWord; +begin + P := PWord(Str); + While (P^ <> 0) do begin + P^ := MakeWord(HiByte(P^), LoByte(P^)); + Inc(P); + end; +end; + +{$IFDEF USE_SYSTEM_OVERRIDES} + +//-------------------------------------------------------------------- +// LoadResString() +// +// This system function is used to retrieve a resourcestring and +// return the result as an AnsiString. If we believe that the result +// is only a temporary value, and that it will be immediately +// assigned to a WideString or a Variant, then we will save the +// Unicode result as well as a reference to the original Ansi string. +// WStrFromPCharLen() or VarFromLStr() will return this saved +// Unicode string if it appears to receive the most recent result +// of LoadResString. +//-------------------------------------------------------------------- + + + //=========================================================================================== + // + // function CodeMatchesPatternForUnicode(...); + // + // GIVEN: SomeWideString := SSomeResString; { WideString := resourcestring } + // + // Delphi will compile this statement into the following: + // ------------------------------------------------- + // TempAnsiString := LoadResString(@SSomeResString); + // LINE 1: lea edx,[SomeTempAnsiString] + // LINE 2: mov eax,[@SomeResString] + // LINE 3: call LoadResString + // + // WStrFromLStr(SomeWideString, TempAnsiString); { SomeWideString := TempAnsiString } + // LINE 4: mov edx,[SomeTempAnsiString] + // LINE 5: mov/lea eax [@SomeWideString] + // LINE 6: call @WStrFromLStr + // ------------------------------------------------- + // + // The order in which the parameters are prepared for WStrFromLStr (ie LINE 4 & 5) is + // reversed when assigning a non-temporary AnsiString to a WideString. + // + // This code, for example, results in LINE 4 and LINE 5 being swapped. + // + // SomeAnsiString := SSomeResString; + // SomeWideString := SomeAnsiString; + // + // Since we know the "signature" used by the compiler, we can detect this pattern. + // If we believe it is only temporary, we can save the Unicode results for later + // retrieval from WStrFromLStr. + // + // One final note: When assigning a resourcestring to a Variant, the same patterns exist. + //=========================================================================================== + +function CodeMatchesPatternForUnicode(PLine4: PAnsiChar): Boolean; +const + SIZEOF_OPCODE = 1 {byte}; + MOV_16_OPCODE = AnsiChar($8B); { we'll assume operand size is 16 bits } + MOV_32_OPCODE = AnsiChar($B8); { we'll assume operand size is 32 bits } + LEA_OPCODE = AnsiChar($8D); { operand size can be 16 or 40 bits } + CALL_OPCODE = AnsiChar($E8); { assumed operand size is 32 bits } + BREAK_OPCODE = AnsiChar($CC); {in a breakpoint} +var + PLine1: PAnsiChar; + PLine2: PAnsiChar; + PLine3: PAnsiChar; + DataSize: Integer; // bytes in first LEA operand +begin + Result := False; + + PLine3 := PLine4 - SizeOf(CALL_OPCODE) - 4; + PLine2 := PLine3 - SizeOf(MOV_32_OPCODE) - 4; + + // figure PLine1 and operand size + DataSize := 2; { try 16 bit operand for line 1 } + PLine1 := PLine2 - DataSize - SizeOf(LEA_OPCODE); + if (PLine1^ <> LEA_OPCODE) and (not (IsDebugging and (PLine1^ = BREAK_OPCODE))) then + begin + DataSize := 5; { try 40 bit operand for line 1 } + PLine1 := PLine2 - DataSize - SizeOf(LEA_OPCODE); + end; + if (PLine1^ = LEA_OPCODE) or (IsDebugging and (PLine1^ = BREAK_OPCODE)) then + begin + if CompareMem(PLine1 + SIZEOF_OPCODE, PLine4 + SIZEOF_OPCODE, DataSize) then + begin + // After this check, it seems to match the WideString <- (temp) AnsiString pattern + Result := True; // It is probably OK. (The side effects of being wrong aren't very bad.) + end; + end; +end; + +threadvar + PLastResString: PAnsiChar; + LastResStringValue: AnsiString; + LastWideResString: WideString; + +procedure FreeTntSystemThreadVars; +begin + LastResStringValue := ''; + LastWideResString := ''; +end; + +procedure Custom_System_EndThread(ExitCode: Integer); +begin + FreeTntSystemThreadVars; + {$IFDEF COMPILER_10_UP} + if Assigned(SystemThreadEndProc) then + SystemThreadEndProc(ExitCode); + {$ENDIF} + ExitThread(ExitCode); +end; + +function Custom_System_LoadResString(ResStringRec: PResStringRec): AnsiString; +var + ReturnAddr: Pointer; +begin + // get return address + asm + PUSH ECX + MOV ECX, [EBP + 4] + MOV ReturnAddr, ECX + POP ECX + end; + // check calling code pattern + if CodeMatchesPatternForUnicode(ReturnAddr) then begin + // result will probably be assigned to an intermediate AnsiString + // on its way to either a WideString or Variant. + LastWideResString := WideLoadResString(ResStringRec); + Result := LastWideResString; + LastResStringValue := Result; + if Result = '' then + PLastResString := nil + else + PLastResString := PAnsiChar(Result); + end else begin + // result will probably be assigned to an actual AnsiString variable. + PLastResString := nil; + Result := WideLoadResString(ResStringRec); + end; +end; + +//-------------------------------------------------------------------- +// WStrFromPCharLen() +// +// This system function is used to assign an AnsiString to a WideString. +// It has been modified to assign Unicode results from LoadResString. +// Another purpose of this function is to specify the code page. +//-------------------------------------------------------------------- + +procedure Custom_System_WStrFromPCharLen(var Dest: WideString; Source: PAnsiChar; Length: Integer); +var + DestLen: Integer; + Buffer: array[0..2047] of WideChar; + Local_PLastResString: Pointer; +begin + Local_PLastResString := PLastResString; + if (Local_PLastResString <> nil) + and (Local_PLastResString = Source) + and (System.Length(LastResStringValue) = Length) + and (LastResStringValue = Source) then begin + // use last unicode resource string + PLastResString := nil; { clear for further use } + Dest := LastWideResString; + end else begin + if Local_PLastResString <> nil then + PLastResString := nil; { clear for further use } + if Length <= 0 then + begin + Dest := ''; + Exit; + end; + if Length + 1 < High(Buffer) then + begin + DestLen := MultiByteToWideChar(DefaultSystemCodePage, 0, Source, Length, Buffer, + High(Buffer)); + if DestLen > 0 then + begin + SetLength(Dest, DestLen); + Move(Pointer(@Buffer[0])^, Pointer(Dest)^, DestLen * SizeOf(WideChar)); + Exit; + end; + end; + DestLen := (Length + 1); + SetLength(Dest, DestLen); // overallocate, trim later + DestLen := MultiByteToWideChar(DefaultSystemCodePage, 0, Source, Length, Pointer(Dest), + DestLen); + if DestLen < 0 then + DestLen := 0; + SetLength(Dest, DestLen); + end; +end; + +{$IFNDEF COMPILER_9_UP} + +//-------------------------------------------------------------------- +// LStrFromPWCharLen() +// +// This system function is used to assign an WideString to an AnsiString. +// It has not been modified from its original purpose other than to specify the code page. +//-------------------------------------------------------------------- + +procedure Custom_System_LStrFromPWCharLen(var Dest: AnsiString; Source: PWideChar; Length: Integer); +var + DestLen: Integer; + Buffer: array[0..4095] of AnsiChar; +begin + if Length <= 0 then + begin + Dest := ''; + Exit; + end; + if Length + 1 < (High(Buffer) div sizeof(WideChar)) then + begin + DestLen := WideCharToMultiByte(DefaultSystemCodePage, 0, Source, + Length, Buffer, High(Buffer), + nil, nil); + if DestLen >= 0 then + begin + SetLength(Dest, DestLen); + Move(Pointer(@Buffer[0])^, PAnsiChar(Dest)^, DestLen); + Exit; + end; + end; + + DestLen := (Length + 1) * sizeof(WideChar); + SetLength(Dest, DestLen); // overallocate, trim later + DestLen := WideCharToMultiByte(DefaultSystemCodePage, 0, Source, Length, Pointer(Dest), DestLen, + nil, nil); + if DestLen < 0 then + DestLen := 0; + SetLength(Dest, DestLen); +end; + +//-------------------------------------------------------------------- +// WStrToString() +// +// This system function is used to assign an WideString to an short string. +// It has not been modified from its original purpose other than to specify the code page. +//-------------------------------------------------------------------- + +procedure Custom_System_WStrToString(Dest: PShortString; const Source: WideString; MaxLen: Integer); +var + SourceLen, DestLen: Integer; + Buffer: array[0..511] of AnsiChar; +begin + if MaxLen > 255 then MaxLen := 255; + SourceLen := Length(Source); + if SourceLen >= MaxLen then SourceLen := MaxLen; + if SourceLen = 0 then + DestLen := 0 + else begin + DestLen := WideCharToMultiByte(DefaultSystemCodePage, 0, Pointer(Source), SourceLen, + Buffer, SizeOf(Buffer), nil, nil); + if DestLen > MaxLen then DestLen := MaxLen; + end; + Dest^[0] := Chr(DestLen); + if DestLen > 0 then Move(Buffer, Dest^[1], DestLen); +end; + +{$ENDIF} + +//-------------------------------------------------------------------- +// VarFromLStr() +// +// This system function is used to assign an AnsiString to a Variant. +// It has been modified to assign Unicode results from LoadResString. +//-------------------------------------------------------------------- + +procedure Custom_System_VarFromLStr(var V: TVarData; const Value: AnsiString); +const + varDeepData = $BFE8; +var + Local_PLastResString: Pointer; +begin + if (V.VType and varDeepData) <> 0 then + VarClear(PVariant(@V)^); + + Local_PLastResString := PLastResString; + if (Local_PLastResString <> nil) + and (Local_PLastResString = PAnsiChar(Value)) + and (LastResStringValue = Value) then begin + // use last unicode resource string + PLastResString := nil; { clear for further use } + V.VOleStr := nil; + V.VType := varOleStr; + WideString(Pointer(V.VOleStr)) := Copy(LastWideResString, 1, MaxInt); + end else begin + if Local_PLastResString <> nil then + PLastResString := nil; { clear for further use } + V.VString := nil; + V.VType := varString; + AnsiString(V.VString) := Value; + end; +end; + +{$IFNDEF COMPILER_9_UP} + +//-------------------------------------------------------------------- +// WStrCat3() A := B + C; +// +// This system function is used to concatenate two strings into one result. +// This function is added because A := '' + '' doesn't necessarily result in A = ''; +//-------------------------------------------------------------------- + +procedure Custom_System_WStrCat3(var Dest: WideString; const Source1, Source2: WideString); + + function NewWideString(CharLength: Longint): Pointer; + var + _NewWideString: function(CharLength: Longint): Pointer; + begin + asm + PUSH ECX + MOV ECX, offset System.@NewWideString; + MOV _NewWideString, ECX + POP ECX + end; + Result := _NewWideString(CharLength); + end; + + procedure WStrSet(var S: WideString; P: PWideChar); + var + Temp: Pointer; + begin + Temp := Pointer(InterlockedExchange(Integer(S), Integer(P))); + if Temp <> nil then + WideString(Temp) := ''; + end; + +var + Source1Len, Source2Len: Integer; + NewStr: PWideChar; +begin + Source1Len := Length(Source1); + Source2Len := Length(Source2); + if (Source1Len <> 0) or (Source2Len <> 0) then + begin + NewStr := NewWideString(Source1Len + Source2Len); + Move(Pointer(Source1)^, Pointer(NewStr)^, Source1Len * sizeof(WideChar)); + Move(Pointer(Source2)^, NewStr[Source1Len], Source2Len * sizeof(WideChar)); + WStrSet(Dest, NewStr); + end else + Dest := ''; +end; + +{$ENDIF} + +//-------------------------------------------------------------------- +// System proc replacements +//-------------------------------------------------------------------- + +type + POverwrittenData = ^TOverwrittenData; + TOverwrittenData = record + Location: Pointer; + OldCode: array[0..6] of Byte; + end; + +procedure OverwriteProcedure(OldProcedure, NewProcedure: pointer; Data: POverwrittenData = nil); +{ OverwriteProcedure originally from Igor Siticov } +{ Modified by Jacques Garcia Vazquez } +var + x: PAnsiChar; + y: integer; + ov2, ov: cardinal; + p: pointer; +begin + if Assigned(Data) and (Data.Location <> nil) then + exit; { procedure already overwritten } + + // need six bytes in place of 5 + x := PAnsiChar(OldProcedure); + if not VirtualProtect(Pointer(x), 6, PAGE_EXECUTE_READWRITE, @ov) then + RaiseLastOSError; + + // if a jump is present then a redirect is found + // $FF25 = jmp dword ptr [xxx] + // This redirect is normally present in bpl files, but not in exe files + p := OldProcedure; + + if Word(p^) = $25FF then + begin + Inc(Integer(p), 2); // skip the jump + // get the jump address p^ and dereference it p^^ + p := Pointer(Pointer(p^)^); + + // release the memory + if not VirtualProtect(Pointer(x), 6, ov, @ov2) then + RaiseLastOSError; + + // re protect the correct one + x := PAnsiChar(p); + if not VirtualProtect(Pointer(x), 6, PAGE_EXECUTE_READWRITE, @ov) then + RaiseLastOSError; + end; + + if Assigned(Data) then + begin + Move(x^, Data.OldCode, 6); + { Assign Location last so that Location <> nil only if OldCode is properly initialized. } + Data.Location := x; + end; + + x[0] := AnsiChar($E9); + y := integer(NewProcedure) - integer(p) - 5; + x[1] := AnsiChar(y and 255); + x[2] := AnsiChar((y shr 8) and 255); + x[3] := AnsiChar((y shr 16) and 255); + x[4] := AnsiChar((y shr 24) and 255); + + if not VirtualProtect(Pointer(x), 6, ov, @ov2) then + RaiseLastOSError; +end; + +procedure RestoreProcedure(OriginalProc: Pointer; Data: TOverwrittenData); +var + ov, ov2: Cardinal; +begin + if Data.Location <> nil then begin + if not VirtualProtect(Data.Location, 6, PAGE_EXECUTE_READWRITE, @ov) then + RaiseLastOSError; + Move(Data.OldCode, Data.Location^, 6); + if not VirtualProtect(Data.Location, 6, ov, @ov2) then + RaiseLastOSError; + end; +end; + +function Addr_System_EndThread: Pointer; +begin + Result := @System.EndThread; +end; + +function Addr_System_LoadResString: Pointer; +begin + Result := @System.LoadResString{TNT-ALLOW LoadResString}; +end; + +function Addr_System_WStrFromPCharLen: Pointer; +asm + mov eax, offset System.@WStrFromPCharLen; +end; + +{$IFNDEF COMPILER_9_UP} +function Addr_System_LStrFromPWCharLen: Pointer; +asm + mov eax, offset System.@LStrFromPWCharLen; +end; + +function Addr_System_WStrToString: Pointer; +asm + mov eax, offset System.@WStrToString; +end; +{$ENDIF} + +function Addr_System_VarFromLStr: Pointer; +asm + mov eax, offset System.@VarFromLStr; +end; + +function Addr_System_WStrCat3: Pointer; +asm + mov eax, offset System.@WStrCat3; +end; + +var + System_EndThread_Code, + System_LoadResString_Code, + System_WStrFromPCharLen_Code, + {$IFNDEF COMPILER_9_UP} + System_LStrFromPWCharLen_Code, + System_WStrToString_Code, + {$ENDIF} + System_VarFromLStr_Code + {$IFNDEF COMPILER_9_UP} + , + System_WStrCat3_Code, + SysUtils_WideFmtStr_Code + {$ENDIF} + : TOverwrittenData; + +procedure InstallEndThreadOverride; +begin + OverwriteProcedure(Addr_System_EndThread, @Custom_System_EndThread, @System_EndThread_Code); +end; + +procedure InstallStringConversionOverrides; +begin + OverwriteProcedure(Addr_System_WStrFromPCharLen, @Custom_System_WStrFromPCharLen, @System_WStrFromPCharLen_Code); + {$IFNDEF COMPILER_9_UP} + OverwriteProcedure(Addr_System_LStrFromPWCharLen, @Custom_System_LStrFromPWCharLen, @System_LStrFromPWCharLen_Code); + OverwriteProcedure(Addr_System_WStrToString, @Custom_System_WStrToString, @System_WStrToString_Code); + {$ENDIF} +end; + +procedure InstallWideResourceStrings; +begin + OverwriteProcedure(Addr_System_LoadResString, @Custom_System_LoadResString, @System_LoadResString_Code); + OverwriteProcedure(Addr_System_VarFromLStr, @Custom_System_VarFromLStr, @System_VarFromLStr_Code); +end; + +{$IFNDEF COMPILER_9_UP} +procedure InstallWideStringConcatenationFix; +begin + OverwriteProcedure(Addr_System_WStrCat3, @Custom_System_WStrCat3, @System_WStrCat3_Code); +end; + +procedure InstallWideFormatFixes; +begin + OverwriteProcedure(@SysUtils.WideFmtStr, @TntSysUtils.Tnt_WideFmtStr, @SysUtils_WideFmtStr_Code); +end; +{$ENDIF} + +procedure InstallTntSystemUpdates(Updates: TTntSystemUpdateSet = AllTntSystemUpdates); +begin + InstallEndThreadOverride; + if tsWideResourceStrings in Updates then begin + InstallStringConversionOverrides; + InstallWideResourceStrings; + end; + {$IFNDEF COMPILER_9_UP} + if tsFixImplicitCodePage in Updates then begin + InstallStringConversionOverrides; + { CP_ACP is the code page used by the non-Unicode Windows API. } + GDefaultSystemCodePage := CP_ACP{TNT-ALLOW CP_ACP}; + end; + if tsFixWideStrConcat in Updates then begin + InstallWideStringConcatenationFix; + end; + if tsFixWideFormat in Updates then begin + InstallWideFormatFixes; + end; + {$ENDIF} +end; + +{$IFNDEF COMPILER_9_UP} +var + StartupDefaultUserCodePage: Cardinal; +{$ENDIF} + +procedure UninstallSystemOverrides; +begin + RestoreProcedure(Addr_System_EndThread, System_EndThread_Code); + // String Conversion + RestoreProcedure(Addr_System_WStrFromPCharLen, System_WStrFromPCharLen_Code); + {$IFNDEF COMPILER_9_UP} + RestoreProcedure(Addr_System_LStrFromPWCharLen, System_LStrFromPWCharLen_Code); + RestoreProcedure(Addr_System_WStrToString, System_WStrToString_Code); + GDefaultSystemCodePage := StartupDefaultUserCodePage; + {$ENDIF} + // Wide resourcestring + RestoreProcedure(Addr_System_LoadResString, System_LoadResString_Code); + RestoreProcedure(Addr_System_VarFromLStr, System_VarFromLStr_Code); + {$IFNDEF COMPILER_9_UP} + // WideString concat fix + RestoreProcedure(Addr_System_WStrCat3, System_WStrCat3_Code); + // WideFormat fixes + RestoreProcedure(@SysUtils.WideFmtStr, SysUtils_WideFmtStr_Code); + {$ENDIF} +end; + +{$ENDIF USE_SYSTEM_OVERRIDES} + +initialization + {$IFDEF COMPILER_9_UP} + {$DEFINE USE_GETACP} + {$ENDIF} + {$IFDEF FPC} + {$DEFINE USE_GETACP} + {$ENDIF} + {$IFDEF USE_GETACP} + GDefaultSystemCodePage := GetACP; + {$ELSE} + {$IFDEF COMPILER_7_UP} + if (Win32Platform = VER_PLATFORM_WIN32_NT) and (Win32MajorVersion >= 5) then + GDefaultSystemCodePage := CP_THREAD_ACP // Win 2K/XP/... + else + GDefaultSystemCodePage := LCIDToCodePage(GetThreadLocale); // Win NT4/95/98/ME + {$ELSE} + GDefaultSystemCodePage := CP_ACP{TNT-ALLOW CP_ACP}; + {$ENDIF} + {$ENDIF} + {$IFDEF USE_SYSTEM_OVERRIDES} + {$IFNDEF COMPILER_9_UP} + StartupDefaultUserCodePage := DefaultSystemCodePage; + {$ENDIF} + IsDebugging := DebugHook > 0; + {$ENDIF USE_SYSTEM_OVERRIDES} + +finalization + {$IFDEF USE_SYSTEM_OVERRIDES} + UninstallSystemOverrides; + FreeTntSystemThreadVars; { Make MemorySleuth happy. } + {$ENDIF USE_SYSTEM_OVERRIDES} + +end. diff --git a/Lua/src/lib/TntUnicodeControls/TntWideStrUtils.pas b/Lua/src/lib/TntUnicodeControls/TntWideStrUtils.pas new file mode 100644 index 00000000..99f63aea --- /dev/null +++ b/Lua/src/lib/TntUnicodeControls/TntWideStrUtils.pas @@ -0,0 +1,455 @@ + +{*****************************************************************************} +{ } +{ Tnt Delphi Unicode Controls } +{ http://www.tntware.com/delphicontrols/unicode/ } +{ Version: 2.3.0 } +{ } +{ Copyright (c) 2002-2007, Troy Wolbrink (troy.wolbrink@tntware.com) } +{ } +{*****************************************************************************} + +unit TntWideStrUtils; + +{$IFDEF FPC} + {$MODE Delphi} +{$ENDIF} + +{$INCLUDE TntCompilers.inc} + +interface + +{ Wide string manipulation functions } + +{$IFNDEF COMPILER_9_UP} +function WStrAlloc(Size: Cardinal): PWideChar; +function WStrBufSize(const Str: PWideChar): Cardinal; +{$ENDIF} +{$IFNDEF COMPILER_10_UP} +function WStrMove(Dest: PWideChar; const Source: PWideChar; Count: Cardinal): PWideChar; +{$ENDIF} +{$IFNDEF COMPILER_9_UP} +function WStrNew(const Str: PWideChar): PWideChar; +procedure WStrDispose(Str: PWideChar); +{$ENDIF} +//--------------------------------------------------------------------------------------------- +{$IFNDEF COMPILER_9_UP} +function WStrLen(Str: PWideChar): Cardinal; +function WStrEnd(Str: PWideChar): PWideChar; +{$ENDIF} +{$IFNDEF COMPILER_10_UP} +function WStrCat(Dest: PWideChar; const Source: PWideChar): PWideChar; +{$ENDIF} +{$IFNDEF COMPILER_9_UP} +function WStrCopy(Dest, Source: PWideChar): PWideChar; +function WStrLCopy(Dest, Source: PWideChar; MaxLen: Cardinal): PWideChar; +function WStrPCopy(Dest: PWideChar; const Source: WideString): PWideChar; +function WStrPLCopy(Dest: PWideChar; const Source: WideString; MaxLen: Cardinal): PWideChar; +{$ENDIF} +{$IFNDEF COMPILER_10_UP} +function WStrScan(const Str: PWideChar; Chr: WideChar): PWideChar; +// WStrComp and WStrPos were introduced as broken in Delphi 2006, but fixed in Delphi 2006 Update 2 +function WStrComp(Str1, Str2: PWideChar): Integer; +function WStrPos(Str, SubStr: PWideChar): PWideChar; +{$ENDIF} +function Tnt_WStrComp(Str1, Str2: PWideChar): Integer; deprecated; +function Tnt_WStrPos(Str, SubStr: PWideChar): PWideChar; deprecated; + +{ ------------ introduced --------------- } +function WStrECopy(Dest, Source: PWideChar): PWideChar; +function WStrLComp(Str1, Str2: PWideChar; MaxLen: Cardinal): Integer; +function WStrLIComp(Str1, Str2: PWideChar; MaxLen: Cardinal): Integer; +function WStrIComp(Str1, Str2: PWideChar): Integer; +function WStrLower(Str: PWideChar): PWideChar; +function WStrUpper(Str: PWideChar): PWideChar; +function WStrRScan(const Str: PWideChar; Chr: WideChar): PWideChar; +function WStrLCat(Dest: PWideChar; const Source: PWideChar; MaxLen: Cardinal): PWideChar; +function WStrPas(const Str: PWideChar): WideString; + +{ SysUtils.pas } //------------------------------------------------------------------------- + +{$IFNDEF COMPILER_10_UP} +function WideLastChar(const S: WideString): PWideChar; +function WideQuotedStr(const S: WideString; Quote: WideChar): WideString; +{$ENDIF} +{$IFNDEF COMPILER_9_UP} +function WideExtractQuotedStr(var Src: PWideChar; Quote: WideChar): Widestring; +{$ENDIF} +{$IFNDEF COMPILER_10_UP} +function WideDequotedStr(const S: WideString; AQuote: WideChar): WideString; +{$ENDIF} + +implementation + +uses + {$IFDEF COMPILER_9_UP} WideStrUtils, {$ENDIF} Math, Windows, TntWindows; + +{$IFNDEF COMPILER_9_UP} +function WStrAlloc(Size: Cardinal): PWideChar; +begin + Size := SizeOf(Cardinal) + (Size * SizeOf(WideChar)); + GetMem(Result, Size); + PCardinal(Result)^ := Size; + Inc(PAnsiChar(Result), SizeOf(Cardinal)); +end; + +function WStrBufSize(const Str: PWideChar): Cardinal; +var + P: PWideChar; +begin + P := Str; + Dec(PAnsiChar(P), SizeOf(Cardinal)); + Result := PCardinal(P)^ - SizeOf(Cardinal); + Result := Result div SizeOf(WideChar); +end; +{$ENDIF} + +{$IFNDEF COMPILER_10_UP} +function WStrMove(Dest: PWideChar; const Source: PWideChar; Count: Cardinal): PWideChar; +var + Length: Integer; +begin + Result := Dest; + Length := Count * SizeOf(WideChar); + Move(Source^, Dest^, Length); +end; +{$ENDIF} + +{$IFNDEF COMPILER_9_UP} +function WStrNew(const Str: PWideChar): PWideChar; +var + Size: Cardinal; +begin + if Str = nil then Result := nil else + begin + Size := WStrLen(Str) + 1; + Result := WStrMove(WStrAlloc(Size), Str, Size); + end; +end; + +procedure WStrDispose(Str: PWideChar); +begin + if Str <> nil then + begin + Dec(PAnsiChar(Str), SizeOf(Cardinal)); + FreeMem(Str, Cardinal(Pointer(Str)^)); + end; +end; +{$ENDIF} + +//--------------------------------------------------------------------------------------------- + +{$IFNDEF COMPILER_9_UP} +function WStrLen(Str: PWideChar): Cardinal; +begin + Result := WStrEnd(Str) - Str; +end; + +function WStrEnd(Str: PWideChar): PWideChar; +begin + // returns a pointer to the end of a null terminated string + Result := Str; + While Result^ <> #0 do + Inc(Result); +end; +{$ENDIF} + +{$IFNDEF COMPILER_10_UP} +function WStrCat(Dest: PWideChar; const Source: PWideChar): PWideChar; +begin + Result := Dest; + WStrCopy(WStrEnd(Dest), Source); +end; +{$ENDIF} + +{$IFNDEF COMPILER_9_UP} +function WStrCopy(Dest, Source: PWideChar): PWideChar; +begin + Result := WStrLCopy(Dest, Source, MaxInt); +end; + +function WStrLCopy(Dest, Source: PWideChar; MaxLen: Cardinal): PWideChar; +var + Count: Cardinal; +begin + // copies a specified maximum number of characters from Source to Dest + Result := Dest; + Count := 0; + While (Count < MaxLen) and (Source^ <> #0) do begin + Dest^ := Source^; + Inc(Source); + Inc(Dest); + Inc(Count); + end; + Dest^ := #0; +end; + +function WStrPCopy(Dest: PWideChar; const Source: WideString): PWideChar; +begin + Result := WStrLCopy(Dest, PWideChar(Source), Length(Source)); +end; + +function WStrPLCopy(Dest: PWideChar; const Source: WideString; MaxLen: Cardinal): PWideChar; +begin + Result := WStrLCopy(Dest, PWideChar(Source), MaxLen); +end; +{$ENDIF} + +{$IFNDEF COMPILER_10_UP} +function WStrScan(const Str: PWideChar; Chr: WideChar): PWideChar; +begin + Result := Str; + while Result^ <> Chr do + begin + if Result^ = #0 then + begin + Result := nil; + Exit; + end; + Inc(Result); + end; +end; + +function WStrComp(Str1, Str2: PWideChar): Integer; +begin + Result := WStrLComp(Str1, Str2, MaxInt); +end; + +function WStrPos(Str, SubStr: PWideChar): PWideChar; +var + PSave: PWideChar; + P: PWideChar; + PSub: PWideChar; +begin + // returns a pointer to the first occurance of SubStr in Str + Result := nil; + if (Str <> nil) and (Str^ <> #0) and (SubStr <> nil) and (SubStr^ <> #0) then begin + P := Str; + While P^ <> #0 do begin + if P^ = SubStr^ then begin + // investigate possibility here + PSave := P; + PSub := SubStr; + While (P^ = PSub^) do begin + Inc(P); + Inc(PSub); + if (PSub^ = #0) then begin + Result := PSave; + exit; // found a match + end; + if (P^ = #0) then + exit; // no match, hit end of string + end; + P := PSave; + end; + Inc(P); + end; + end; +end; +{$ENDIF} + +function Tnt_WStrComp(Str1, Str2: PWideChar): Integer; deprecated; +begin + Result := WStrComp(Str1, Str2); +end; + +function Tnt_WStrPos(Str, SubStr: PWideChar): PWideChar; deprecated; +begin + Result := WStrPos(Str, SubStr); +end; + +//------------------------------------------------------------------------------ + +function WStrECopy(Dest, Source: PWideChar): PWideChar; +begin + Result := WStrEnd(WStrCopy(Dest, Source)); +end; + +function WStrComp_EX(Str1, Str2: PWideChar; MaxLen: Cardinal; dwCmpFlags: Cardinal): Integer; +var + Len1, Len2: Integer; +begin + if MaxLen = Cardinal(MaxInt) then begin + Len1 := -1; + Len2 := -1; + end else begin + Len1 := Min(WStrLen(Str1), MaxLen); + Len2 := Min(WStrLen(Str2), MaxLen); + end; + Result := Tnt_CompareStringW(GetThreadLocale, dwCmpFlags, Str1, Len1, Str2, Len2) - 2; +end; + +function WStrLComp(Str1, Str2: PWideChar; MaxLen: Cardinal): Integer; +begin + Result := WStrComp_EX(Str1, Str2, MaxLen, 0); +end; + +function WStrLIComp(Str1, Str2: PWideChar; MaxLen: Cardinal): Integer; +begin + Result := WStrComp_EX(Str1, Str2, MaxLen, NORM_IGNORECASE); +end; + +function WStrIComp(Str1, Str2: PWideChar): Integer; +begin + Result := WStrLIComp(Str1, Str2, MaxInt); +end; + +function WStrLower(Str: PWideChar): PWideChar; +begin + Result := Str; + Tnt_CharLowerBuffW(Str, WStrLen(Str)) +end; + +function WStrUpper(Str: PWideChar): PWideChar; +begin + Result := Str; + Tnt_CharUpperBuffW(Str, WStrLen(Str)) +end; + +function WStrRScan(const Str: PWideChar; Chr: WideChar): PWideChar; +var + MostRecentFound: PWideChar; +begin + if Chr = #0 then + Result := WStrEnd(Str) + else + begin + Result := nil; + MostRecentFound := Str; + while True do + begin + while MostRecentFound^ <> Chr do + begin + if MostRecentFound^ = #0 then + Exit; + Inc(MostRecentFound); + end; + Result := MostRecentFound; + Inc(MostRecentFound); + end; + end; +end; + +function WStrLCat(Dest: PWideChar; const Source: PWideChar; MaxLen: Cardinal): PWideChar; +begin + Result := Dest; + WStrLCopy(WStrEnd(Dest), Source, MaxLen - WStrLen(Dest)); +end; + +function WStrPas(const Str: PWideChar): WideString; +begin + Result := Str; +end; + +//--------------------------------------------------------------------------------------------- + +{$IFNDEF COMPILER_10_UP} +function WideLastChar(const S: WideString): PWideChar; +begin + if S = '' then + Result := nil + else + Result := @S[Length(S)]; +end; + +function WideQuotedStr(const S: WideString; Quote: WideChar): WideString; +var + P, Src, + Dest: PWideChar; + AddCount: Integer; +begin + AddCount := 0; + P := WStrScan(PWideChar(S), Quote); + while (P <> nil) do + begin + Inc(P); + Inc(AddCount); + P := WStrScan(P, Quote); + end; + + if AddCount = 0 then + Result := Quote + S + Quote + else + begin + SetLength(Result, Length(S) + AddCount + 2); + Dest := PWideChar(Result); + Dest^ := Quote; + Inc(Dest); + Src := PWideChar(S); + P := WStrScan(Src, Quote); + repeat + Inc(P); + Move(Src^, Dest^, 2 * (P - Src)); + Inc(Dest, P - Src); + Dest^ := Quote; + Inc(Dest); + Src := P; + P := WStrScan(Src, Quote); + until P = nil; + P := WStrEnd(Src); + Move(Src^, Dest^, 2 * (P - Src)); + Inc(Dest, P - Src); + Dest^ := Quote; + end; +end; +{$ENDIF} + +{$IFNDEF COMPILER_9_UP} +function WideExtractQuotedStr(var Src: PWideChar; Quote: WideChar): Widestring; +var + P, Dest: PWideChar; + DropCount: Integer; +begin + Result := ''; + if (Src = nil) or (Src^ <> Quote) then Exit; + Inc(Src); + DropCount := 1; + P := Src; + Src := WStrScan(Src, Quote); + while Src <> nil do // count adjacent pairs of quote chars + begin + Inc(Src); + if Src^ <> Quote then Break; + Inc(Src); + Inc(DropCount); + Src := WStrScan(Src, Quote); + end; + if Src = nil then Src := WStrEnd(P); + if ((Src - P) <= 1) then Exit; + if DropCount = 1 then + SetString(Result, P, Src - P - 1) + else + begin + SetLength(Result, Src - P - DropCount); + Dest := PWideChar(Result); + Src := WStrScan(P, Quote); + while Src <> nil do + begin + Inc(Src); + if Src^ <> Quote then Break; + Move(P^, Dest^, (Src - P) * SizeOf(WideChar)); + Inc(Dest, Src - P); + Inc(Src); + P := Src; + Src := WStrScan(Src, Quote); + end; + if Src = nil then Src := WStrEnd(P); + Move(P^, Dest^, (Src - P - 1) * SizeOf(WideChar)); + end; +end; +{$ENDIF} + +{$IFNDEF COMPILER_10_UP} +function WideDequotedStr(const S: WideString; AQuote: WideChar): WideString; +var + LText : PWideChar; +begin + LText := PWideChar(S); + Result := WideExtractQuotedStr(LText, AQuote); + if Result = '' then + Result := S; +end; +{$ENDIF} + + +end. diff --git a/Lua/src/lib/TntUnicodeControls/TntWideStrings.pas b/Lua/src/lib/TntUnicodeControls/TntWideStrings.pas new file mode 100644 index 00000000..75132d22 --- /dev/null +++ b/Lua/src/lib/TntUnicodeControls/TntWideStrings.pas @@ -0,0 +1,846 @@ + +{*****************************************************************************} +{ } +{ Tnt Delphi Unicode Controls } +{ http://www.tntware.com/delphicontrols/unicode/ } +{ Version: 2.3.0 } +{ } +{ Copyright (c) 2002-2007, Troy Wolbrink (troy.wolbrink@tntware.com) } +{ } +{*****************************************************************************} + +unit TntWideStrings; + +{$IFDEF FPC} + {$MODE Delphi} +{$ENDIF} + +{$INCLUDE TntCompilers.inc} + +interface + +{$IFDEF COMPILER_10_UP} + {$MESSAGE FATAL 'Do not refer to TntWideStrings.pas. It works correctly in Delphi 2006.'} +{$ENDIF} + +uses + Classes; + +{******************************************************************************} +{ } +{ Delphi 2005 introduced TWideStrings in WideStrings.pas. } +{ Unfortunately, it was not ready for prime time. } +{ Setting CommaText is not consistent, and it relies on CharNextW } +{ Which is only available on Windows NT+. } +{ } +{******************************************************************************} + +type + TWideStrings = class; + +{ IWideStringsAdapter interface } +{ Maintains link between TWideStrings and IWideStrings implementations } + + IWideStringsAdapter = interface + ['{25FE0E3B-66CB-48AA-B23B-BCFA67E8F5DA}'] + procedure ReferenceStrings(S: TWideStrings); + procedure ReleaseStrings; + end; + + TWideStringsEnumerator = class + private + FIndex: Integer; + FStrings: TWideStrings; + public + constructor Create(AStrings: TWideStrings); + function GetCurrent: WideString; + function MoveNext: Boolean; + property Current: WideString read GetCurrent; + end; + +{$IFDEF FPC} + TStringsDefined = set of ( + sdDelimiter, sdQuoteChar, sdNameValueSeparator, sdLineBreak, + sdStrictDelimiter); +{$ENDIF} + +{$DEFINE NAMEVALUESEPARATOR_RW} +{$IFNDEF COMPILER_7_UP} + {$UNDEF NAMEVALUESEPARATOR_RW} +{$ENDIF} + +{ TWideStrings class } + + TWideStrings = class(TPersistent) + private + FDefined: TStringsDefined; + FDelimiter: WideChar; + FQuoteChar: WideChar; + {$IFDEF NAMEVALUESEPARATOR_RW} + FNameValueSeparator: WideChar; + {$ENDIF} + FUpdateCount: Integer; + FAdapter: IWideStringsAdapter; + function GetCommaText: WideString; + function GetDelimitedText: WideString; + function GetName(Index: Integer): WideString; + function GetValue(const Name: WideString): WideString; + procedure ReadData(Reader: TReader); + procedure SetCommaText(const Value: WideString); + procedure SetDelimitedText(const Value: WideString); + procedure SetStringsAdapter(const Value: IWideStringsAdapter); + procedure SetValue(const Name, Value: WideString); + procedure WriteData(Writer: TWriter); + function GetDelimiter: WideChar; + procedure SetDelimiter(const Value: WideChar); + function GetQuoteChar: WideChar; + procedure SetQuoteChar(const Value: WideChar); + function GetNameValueSeparator: WideChar; + {$IFDEF NAMEVALUESEPARATOR_RW} + procedure SetNameValueSeparator(const Value: WideChar); + {$ENDIF} + function GetValueFromIndex(Index: Integer): WideString; + procedure SetValueFromIndex(Index: Integer; const Value: WideString); + protected + procedure AssignTo(Dest: TPersistent); override; + procedure DefineProperties(Filer: TFiler); override; + procedure Error(const Msg: WideString; Data: Integer); overload; + procedure Error(Msg: PResStringRec; Data: Integer); overload; + function ExtractName(const S: WideString): WideString; + function Get(Index: Integer): WideString; virtual; abstract; + function GetCapacity: Integer; virtual; + function GetCount: Integer; virtual; abstract; + function GetObject(Index: Integer): TObject; virtual; + function GetTextStr: WideString; virtual; + procedure Put(Index: Integer; const S: WideString); virtual; + procedure PutObject(Index: Integer; AObject: TObject); virtual; + procedure SetCapacity(NewCapacity: Integer); virtual; + procedure SetTextStr(const Value: WideString); virtual; + procedure SetUpdateState(Updating: Boolean); virtual; + property UpdateCount: Integer read FUpdateCount; + function CompareStrings(const S1, S2: WideString): Integer; virtual; + public + destructor Destroy; override; + function Add(const S: WideString): Integer; virtual; + function AddObject(const S: WideString; AObject: TObject): Integer; virtual; + procedure Append(const S: WideString); + procedure AddStrings(Strings: TStrings{TNT-ALLOW TStrings}); overload; virtual; + procedure AddStrings(Strings: TWideStrings); overload; virtual; + procedure Assign(Source: TPersistent); override; + procedure BeginUpdate; + procedure Clear; virtual; abstract; + procedure Delete(Index: Integer); virtual; abstract; + procedure EndUpdate; + function Equals(Strings: TWideStrings): Boolean; + procedure Exchange(Index1, Index2: Integer); virtual; + function GetEnumerator: TWideStringsEnumerator; + function GetTextW: PWideChar; virtual; + function IndexOf(const S: WideString): Integer; virtual; + function IndexOfName(const Name: WideString): Integer; virtual; + function IndexOfObject(AObject: TObject): Integer; virtual; + procedure Insert(Index: Integer; const S: WideString); virtual; abstract; + procedure InsertObject(Index: Integer; const S: WideString; + AObject: TObject); virtual; + procedure LoadFromFile(const FileName: WideString); virtual; + procedure LoadFromStream(Stream: TStream); virtual; + procedure Move(CurIndex, NewIndex: Integer); virtual; + procedure SaveToFile(const FileName: WideString); virtual; + procedure SaveToStream(Stream: TStream); virtual; + procedure SetTextW(const Text: PWideChar); virtual; + property Capacity: Integer read GetCapacity write SetCapacity; + property CommaText: WideString read GetCommaText write SetCommaText; + property Count: Integer read GetCount; + property Delimiter: WideChar read GetDelimiter write SetDelimiter; + property DelimitedText: WideString read GetDelimitedText write SetDelimitedText; + property Names[Index: Integer]: WideString read GetName; + property Objects[Index: Integer]: TObject read GetObject write PutObject; + property QuoteChar: WideChar read GetQuoteChar write SetQuoteChar; + property Values[const Name: WideString]: WideString read GetValue write SetValue; + property ValueFromIndex[Index: Integer]: WideString read GetValueFromIndex write SetValueFromIndex; + property NameValueSeparator: WideChar read GetNameValueSeparator {$IFDEF NAMEVALUESEPARATOR_RW} write SetNameValueSeparator {$ENDIF}; + property Strings[Index: Integer]: WideString read Get write Put; default; + property Text: WideString read GetTextStr write SetTextStr; + property StringsAdapter: IWideStringsAdapter read FAdapter write SetStringsAdapter; + end; + + PWideStringItem = ^TWideStringItem; + TWideStringItem = record + FString: WideString; + FObject: TObject; + end; + + PWideStringItemList = ^TWideStringItemList; + TWideStringItemList = array[0..MaxListSize] of TWideStringItem; + +implementation + +uses + Windows, SysUtils, TntSystem, {$IFDEF COMPILER_9_UP} WideStrUtils, {$ELSE} TntWideStrUtils, {$ENDIF} + TntSysUtils, TntClasses; + +{ TWideStringsEnumerator } + +constructor TWideStringsEnumerator.Create(AStrings: TWideStrings); +begin + inherited Create; + FIndex := -1; + FStrings := AStrings; +end; + +function TWideStringsEnumerator.GetCurrent: WideString; +begin + Result := FStrings[FIndex]; +end; + +function TWideStringsEnumerator.MoveNext: Boolean; +begin + Result := FIndex < FStrings.Count - 1; + if Result then + Inc(FIndex); +end; + +{ TWideStrings } + +destructor TWideStrings.Destroy; +begin + StringsAdapter := nil; + inherited; +end; + +function TWideStrings.Add(const S: WideString): Integer; +begin + Result := GetCount; + Insert(Result, S); +end; + +function TWideStrings.AddObject(const S: WideString; AObject: TObject): Integer; +begin + Result := Add(S); + PutObject(Result, AObject); +end; + +procedure TWideStrings.Append(const S: WideString); +begin + Add(S); +end; + +procedure TWideStrings.AddStrings(Strings: TStrings{TNT-ALLOW TStrings}); +var + I: Integer; +begin + BeginUpdate; + try + for I := 0 to Strings.Count - 1 do + AddObject(Strings[I], Strings.Objects[I]); + finally + EndUpdate; + end; +end; + +procedure TWideStrings.AddStrings(Strings: TWideStrings); +var + I: Integer; +begin + BeginUpdate; + try + for I := 0 to Strings.Count - 1 do + AddObject(Strings[I], Strings.Objects[I]); + finally + EndUpdate; + end; +end; + +procedure TWideStrings.Assign(Source: TPersistent); +begin + if Source is TWideStrings then + begin + BeginUpdate; + try + Clear; + FDefined := TWideStrings(Source).FDefined; + {$IFDEF NAMEVALUESEPARATOR_RW} + FNameValueSeparator := TWideStrings(Source).FNameValueSeparator; + {$ENDIF} + FQuoteChar := TWideStrings(Source).FQuoteChar; + FDelimiter := TWideStrings(Source).FDelimiter; + AddStrings(TWideStrings(Source)); + finally + EndUpdate; + end; + end + else if Source is TStrings{TNT-ALLOW TStrings} then + begin + BeginUpdate; + try + Clear; + {$IFDEF NAMEVALUESEPARATOR_RW} + FNameValueSeparator := WideChar(TStrings{TNT-ALLOW TStrings}(Source).NameValueSeparator); + {$ENDIF} + FQuoteChar := WideChar(TStrings{TNT-ALLOW TStrings}(Source).QuoteChar); + FDelimiter := WideChar(TStrings{TNT-ALLOW TStrings}(Source).Delimiter); + AddStrings(TStrings{TNT-ALLOW TStrings}(Source)); + finally + EndUpdate; + end; + end + else + inherited Assign(Source); +end; + +procedure TWideStrings.AssignTo(Dest: TPersistent); +var + I: Integer; +begin + if Dest is TWideStrings then Dest.Assign(Self) + else if Dest is TStrings{TNT-ALLOW TStrings} then + begin + TStrings{TNT-ALLOW TStrings}(Dest).BeginUpdate; + try + TStrings{TNT-ALLOW TStrings}(Dest).Clear; + {$IFDEF NAMEVALUESEPARATOR_RW} + TStrings{TNT-ALLOW TStrings}(Dest).NameValueSeparator := AnsiChar(NameValueSeparator); + {$ENDIF} + TStrings{TNT-ALLOW TStrings}(Dest).QuoteChar := AnsiChar(QuoteChar); + TStrings{TNT-ALLOW TStrings}(Dest).Delimiter := AnsiChar(Delimiter); + for I := 0 to Count - 1 do + TStrings{TNT-ALLOW TStrings}(Dest).AddObject(Strings[I], Objects[I]); + finally + TStrings{TNT-ALLOW TStrings}(Dest).EndUpdate; + end; + end + else + inherited AssignTo(Dest); +end; + +procedure TWideStrings.BeginUpdate; +begin + if FUpdateCount = 0 then SetUpdateState(True); + Inc(FUpdateCount); +end; + +procedure TWideStrings.DefineProperties(Filer: TFiler); + + function DoWrite: Boolean; + begin + if Filer.Ancestor <> nil then + begin + Result := True; + if Filer.Ancestor is TWideStrings then + Result := not Equals(TWideStrings(Filer.Ancestor)) + end + else Result := Count > 0; + end; + +begin + Filer.DefineProperty('Strings', ReadData, WriteData, DoWrite); +end; + +procedure TWideStrings.EndUpdate; +begin + Dec(FUpdateCount); + if FUpdateCount = 0 then SetUpdateState(False); +end; + +function TWideStrings.Equals(Strings: TWideStrings): Boolean; +var + I, Count: Integer; +begin + Result := False; + Count := GetCount; + if Count <> Strings.GetCount then Exit; + for I := 0 to Count - 1 do if Get(I) <> Strings.Get(I) then Exit; + Result := True; +end; + +procedure TWideStrings.Error(const Msg: WideString; Data: Integer); + + function ReturnAddr: Pointer; + asm + MOV EAX,[EBP+4] + end; + +begin + raise EStringListError.CreateFmt(Msg, [Data]) at ReturnAddr; +end; + +procedure TWideStrings.Error(Msg: PResStringRec; Data: Integer); +begin + Error(WideLoadResString(Msg), Data); +end; + +procedure TWideStrings.Exchange(Index1, Index2: Integer); +var + TempObject: TObject; + TempString: WideString; +begin + BeginUpdate; + try + TempString := Strings[Index1]; + TempObject := Objects[Index1]; + Strings[Index1] := Strings[Index2]; + Objects[Index1] := Objects[Index2]; + Strings[Index2] := TempString; + Objects[Index2] := TempObject; + finally + EndUpdate; + end; +end; + +function TWideStrings.ExtractName(const S: WideString): WideString; +var + P: Integer; +begin + Result := S; + P := Pos(NameValueSeparator, Result); + if P <> 0 then + SetLength(Result, P-1) else + SetLength(Result, 0); +end; + +function TWideStrings.GetCapacity: Integer; +begin // descendents may optionally override/replace this default implementation + Result := Count; +end; + +function TWideStrings.GetCommaText: WideString; +var + LOldDefined: TStringsDefined; + LOldDelimiter: WideChar; + LOldQuoteChar: WideChar; +begin + LOldDefined := FDefined; + LOldDelimiter := FDelimiter; + LOldQuoteChar := FQuoteChar; + Delimiter := ','; + QuoteChar := '"'; + try + Result := GetDelimitedText; + finally + FDelimiter := LOldDelimiter; + FQuoteChar := LOldQuoteChar; + FDefined := LOldDefined; + end; +end; + +function TWideStrings.GetDelimitedText: WideString; +var + S: WideString; + P: PWideChar; + I, Count: Integer; +begin + Count := GetCount; + if (Count = 1) and (Get(0) = '') then + Result := WideString(QuoteChar) + QuoteChar + else + begin + Result := ''; + for I := 0 to Count - 1 do + begin + S := Get(I); + P := PWideChar(S); + while not ((P^ in [WideChar(#0)..WideChar(' ')]) or (P^ = QuoteChar) or (P^ = Delimiter)) do + Inc(P); + if (P^ <> #0) then S := WideQuotedStr(S, QuoteChar); + Result := Result + S + Delimiter; + end; + System.Delete(Result, Length(Result), 1); + end; +end; + +function TWideStrings.GetName(Index: Integer): WideString; +begin + Result := ExtractName(Get(Index)); +end; + +function TWideStrings.GetObject(Index: Integer): TObject; +begin + Result := nil; +end; + +function TWideStrings.GetEnumerator: TWideStringsEnumerator; +begin + Result := TWideStringsEnumerator.Create(Self); +end; + +function TWideStrings.GetTextW: PWideChar; +begin + Result := WStrNew(PWideChar(GetTextStr)); +end; + +function TWideStrings.GetTextStr: WideString; +var + I, L, Size, Count: Integer; + P: PWideChar; + S, LB: WideString; +begin + Count := GetCount; + Size := 0; + LB := sLineBreak; + for I := 0 to Count - 1 do Inc(Size, Length(Get(I)) + Length(LB)); + SetString(Result, nil, Size); + P := Pointer(Result); + for I := 0 to Count - 1 do + begin + S := Get(I); + L := Length(S); + if L <> 0 then + begin + System.Move(Pointer(S)^, P^, L * SizeOf(WideChar)); + Inc(P, L); + end; + L := Length(LB); + if L <> 0 then + begin + System.Move(Pointer(LB)^, P^, L * SizeOf(WideChar)); + Inc(P, L); + end; + end; +end; + +function TWideStrings.GetValue(const Name: WideString): WideString; +var + I: Integer; +begin + I := IndexOfName(Name); + if I >= 0 then + Result := Copy(Get(I), Length(Name) + 2, MaxInt) else + Result := ''; +end; + +function TWideStrings.IndexOf(const S: WideString): Integer; +begin + for Result := 0 to GetCount - 1 do + if CompareStrings(Get(Result), S) = 0 then Exit; + Result := -1; +end; + +function TWideStrings.IndexOfName(const Name: WideString): Integer; +var + P: Integer; + S: WideString; +begin + for Result := 0 to GetCount - 1 do + begin + S := Get(Result); + P := Pos(NameValueSeparator, S); + if (P <> 0) and (CompareStrings(Copy(S, 1, P - 1), Name) = 0) then Exit; + end; + Result := -1; +end; + +function TWideStrings.IndexOfObject(AObject: TObject): Integer; +begin + for Result := 0 to GetCount - 1 do + if GetObject(Result) = AObject then Exit; + Result := -1; +end; + +procedure TWideStrings.InsertObject(Index: Integer; const S: WideString; + AObject: TObject); +begin + Insert(Index, S); + PutObject(Index, AObject); +end; + +procedure TWideStrings.LoadFromFile(const FileName: WideString); +var + Stream: TStream; +begin + Stream := TTntFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite); + try + LoadFromStream(Stream); + finally + Stream.Free; + end; +end; + +procedure TWideStrings.LoadFromStream(Stream: TStream); +var + Size: Integer; + S: WideString; +begin + BeginUpdate; + try + Size := Stream.Size - Stream.Position; + SetString(S, nil, Size div SizeOf(WideChar)); + Stream.Read(Pointer(S)^, Length(S) * SizeOf(WideChar)); + SetTextStr(S); + finally + EndUpdate; + end; +end; + +procedure TWideStrings.Move(CurIndex, NewIndex: Integer); +var + TempObject: TObject; + TempString: WideString; +begin + if CurIndex <> NewIndex then + begin + BeginUpdate; + try + TempString := Get(CurIndex); + TempObject := GetObject(CurIndex); + Delete(CurIndex); + InsertObject(NewIndex, TempString, TempObject); + finally + EndUpdate; + end; + end; +end; + +procedure TWideStrings.Put(Index: Integer; const S: WideString); +var + TempObject: TObject; +begin + TempObject := GetObject(Index); + Delete(Index); + InsertObject(Index, S, TempObject); +end; + +procedure TWideStrings.PutObject(Index: Integer; AObject: TObject); +begin +end; + +procedure TWideStrings.ReadData(Reader: TReader); +begin + if Reader.NextValue in [vaString, vaLString] then + SetTextStr(Reader.ReadString) {JCL compatiblity} + else if Reader.NextValue = vaWString then + SetTextStr(Reader.ReadWideString) {JCL compatiblity} + else begin + BeginUpdate; + try + Clear; + Reader.ReadListBegin; + while not Reader.EndOfList do + if Reader.NextValue in [vaString, vaLString] then + Add(Reader.ReadString) {TStrings compatiblity} + else + Add(Reader.ReadWideString); + Reader.ReadListEnd; + finally + EndUpdate; + end; + end; +end; + +procedure TWideStrings.SaveToFile(const FileName: WideString); +var + Stream: TStream; +begin + Stream := TTntFileStream.Create(FileName, fmCreate); + try + SaveToStream(Stream); + finally + Stream.Free; + end; +end; + +procedure TWideStrings.SaveToStream(Stream: TStream); +var + SW: WideString; +begin + SW := GetTextStr; + Stream.WriteBuffer(PWideChar(SW)^, Length(SW) * SizeOf(WideChar)); +end; + +procedure TWideStrings.SetCapacity(NewCapacity: Integer); +begin + // do nothing - descendents may optionally implement this method +end; + +procedure TWideStrings.SetCommaText(const Value: WideString); +begin + Delimiter := ','; + QuoteChar := '"'; + SetDelimitedText(Value); +end; + +procedure TWideStrings.SetStringsAdapter(const Value: IWideStringsAdapter); +begin + if FAdapter <> nil then FAdapter.ReleaseStrings; + FAdapter := Value; + if FAdapter <> nil then FAdapter.ReferenceStrings(Self); +end; + +procedure TWideStrings.SetTextW(const Text: PWideChar); +begin + SetTextStr(Text); +end; + +procedure TWideStrings.SetTextStr(const Value: WideString); +var + P, Start: PWideChar; + S: WideString; +begin + BeginUpdate; + try + Clear; + P := Pointer(Value); + if P <> nil then + while P^ <> #0 do + begin + Start := P; + while not (P^ in [WideChar(#0), WideChar(#10), WideChar(#13)]) and (P^ <> WideLineSeparator) do + Inc(P); + SetString(S, Start, P - Start); + Add(S); + if P^ = #13 then Inc(P); + if P^ = #10 then Inc(P); + if P^ = WideLineSeparator then Inc(P); + end; + finally + EndUpdate; + end; +end; + +procedure TWideStrings.SetUpdateState(Updating: Boolean); +begin +end; + +procedure TWideStrings.SetValue(const Name, Value: WideString); +var + I: Integer; +begin + I := IndexOfName(Name); + if Value <> '' then + begin + if I < 0 then I := Add(''); + Put(I, Name + NameValueSeparator + Value); + end else + begin + if I >= 0 then Delete(I); + end; +end; + +procedure TWideStrings.WriteData(Writer: TWriter); +var + I: Integer; +begin + Writer.WriteListBegin; + for I := 0 to Count-1 do begin + Writer.WriteWideString(Get(I)); + end; + Writer.WriteListEnd; +end; + +procedure TWideStrings.SetDelimitedText(const Value: WideString); +var + P, P1: PWideChar; + S: WideString; +begin + BeginUpdate; + try + Clear; + P := PWideChar(Value); + while P^ in [WideChar(#1)..WideChar(' ')] do + Inc(P); + while P^ <> #0 do + begin + if P^ = QuoteChar then + S := WideExtractQuotedStr(P, QuoteChar) + else + begin + P1 := P; + while (P^ > ' ') and (P^ <> Delimiter) do + Inc(P); + SetString(S, P1, P - P1); + end; + Add(S); + while P^ in [WideChar(#1)..WideChar(' ')] do + Inc(P); + if P^ = Delimiter then + begin + P1 := P; + Inc(P1); + if P1^ = #0 then + Add(''); + repeat + Inc(P); + until not (P^ in [WideChar(#1)..WideChar(' ')]); + end; + end; + finally + EndUpdate; + end; +end; + +function TWideStrings.GetDelimiter: WideChar; +begin + if not (sdDelimiter in FDefined) then + Delimiter := ','; + Result := FDelimiter; +end; + +function TWideStrings.GetQuoteChar: WideChar; +begin + if not (sdQuoteChar in FDefined) then + QuoteChar := '"'; + Result := FQuoteChar; +end; + +procedure TWideStrings.SetDelimiter(const Value: WideChar); +begin + if (FDelimiter <> Value) or not (sdDelimiter in FDefined) then + begin + Include(FDefined, sdDelimiter); + FDelimiter := Value; + end +end; + +procedure TWideStrings.SetQuoteChar(const Value: WideChar); +begin + if (FQuoteChar <> Value) or not (sdQuoteChar in FDefined) then + begin + Include(FDefined, sdQuoteChar); + FQuoteChar := Value; + end +end; + +function TWideStrings.CompareStrings(const S1, S2: WideString): Integer; +begin + Result := WideCompareText(S1, S2); +end; + +function TWideStrings.GetNameValueSeparator: WideChar; +begin + {$IFDEF NAMEVALUESEPARATOR_RW} + if not (sdNameValueSeparator in FDefined) then + NameValueSeparator := '='; + Result := FNameValueSeparator; + {$ELSE} + Result := '='; + {$ENDIF} +end; + +{$IFDEF NAMEVALUESEPARATOR_RW} +procedure TWideStrings.SetNameValueSeparator(const Value: WideChar); +begin + if (FNameValueSeparator <> Value) or not (sdNameValueSeparator in FDefined) then + begin + Include(FDefined, sdNameValueSeparator); + FNameValueSeparator := Value; + end +end; +{$ENDIF} + +function TWideStrings.GetValueFromIndex(Index: Integer): WideString; +begin + if Index >= 0 then + Result := Copy(Get(Index), Length(Names[Index]) + 2, MaxInt) else + Result := ''; +end; + +procedure TWideStrings.SetValueFromIndex(Index: Integer; const Value: WideString); +begin + if Value <> '' then + begin + if Index < 0 then Index := Add(''); + Put(Index, Names[Index] + NameValueSeparator + Value); + end + else + if Index >= 0 then Delete(Index); +end; + +end. diff --git a/Lua/src/lib/TntUnicodeControls/TntWindows.pas b/Lua/src/lib/TntUnicodeControls/TntWindows.pas new file mode 100644 index 00000000..8fd7ec88 --- /dev/null +++ b/Lua/src/lib/TntUnicodeControls/TntWindows.pas @@ -0,0 +1,1501 @@ + +{*****************************************************************************} +{ } +{ Tnt Delphi Unicode Controls } +{ http://www.tntware.com/delphicontrols/unicode/ } +{ Version: 2.3.0 } +{ } +{ Copyright (c) 2002-2007, Troy Wolbrink (troy.wolbrink@tntware.com) } +{ } +{*****************************************************************************} + +unit TntWindows; + +{$IFDEF FPC} + {$MODE Delphi} +{$ENDIF} + +{$INCLUDE TntCompilers.inc} + +interface + +uses + Windows, ShellApi, ShlObj; + +// ......... compatibility + +const + DT_NOFULLWIDTHCHARBREAK = $00080000; + +const + INVALID_FILE_ATTRIBUTES = DWORD(-1); + +// ................ ANSI TYPES ................ +{TNT-WARN LPSTR} +{TNT-WARN PLPSTR} +{TNT-WARN LPCSTR} +{TNT-WARN LPCTSTR} +{TNT-WARN LPTSTR} + +// ........ EnumResourceTypesW, EnumResourceNamesW and EnumResourceLanguagesW are supposed .... +// ........ to work on Win95/98/ME but have caused access violations in testing on Win95 ...... +// .. TNT--WARN EnumResourceTypes .. +// .. TNT--WARN EnumResourceTypesA .. +// .. TNT--WARN EnumResourceNames .. +// .. TNT--WARN EnumResourceNamesA .. +// .. TNT--WARN EnumResourceLanguages .. +// .. TNT--WARN EnumResourceLanguagesA .. + +//------------------------------------------------------------------------------------------ + +// ......... The Unicode form of these functions are supported on Windows 95/98/ME ......... +{TNT-WARN ExtTextOut} +{TNT-WARN ExtTextOutA} +{TNT-WARN Tnt_ExtTextOutW} + +{TNT-WARN FindResource} +{TNT-WARN FindResourceA} +{TNT-WARN Tnt_FindResourceW} + +{TNT-WARN FindResourceEx} +{TNT-WARN FindResourceExA} +{TNT-WARN Tnt_FindResourceExW} + +{TNT-WARN GetCharWidth} +{TNT-WARN GetCharWidthA} +{TNT-WARN Tnt_GetCharWidthW} + +{TNT-WARN GetCommandLine} +{TNT-WARN GetCommandLineA} +{TNT-WARN Tnt_GetCommandLineW} + +{TNT-WARN GetTextExtentPoint} +{TNT-WARN GetTextExtentPointA} +{TNT-WARN Tnt_GetTextExtentPointW} + +{TNT-WARN GetTextExtentPoint32} +{TNT-WARN GetTextExtentPoint32A} +{TNT-WARN Tnt_GetTextExtentPoint32W} + +{TNT-WARN lstrcat} +{TNT-WARN lstrcatA} +{TNT-WARN Tnt_lstrcatW} + +{TNT-WARN lstrcpy} +{TNT-WARN lstrcpyA} +{TNT-WARN Tnt_lstrcpyW} + +{TNT-WARN lstrlen} +{TNT-WARN lstrlenA} +{TNT-WARN Tnt_lstrlenW} + +{TNT-WARN MessageBox} +{TNT-WARN MessageBoxA} +{TNT-WARN Tnt_MessageBoxW} + +{TNT-WARN MessageBoxEx} +{TNT-WARN MessageBoxExA} +{TNT-WARN Tnt_MessageBoxExA} + +{TNT-WARN TextOut} +{TNT-WARN TextOutA} +{TNT-WARN Tnt_TextOutW} + +//------------------------------------------------------------------------------------------ + +{TNT-WARN LOCALE_USER_DEFAULT} // <-- use GetThreadLocale +{TNT-WARN LOCALE_SYSTEM_DEFAULT} // <-- use GetThreadLocale + +//------------------------------------------------------------------------------------------ +// compatiblity +//------------------------------------------------------------------------------------------ +{$IFNDEF COMPILER_9_UP} +type + {$IFDEF FPC} + TStartupInfoA = STARTUPINFO; + TStartupInfoW = STARTUPINFO; + {$ELSE} + TStartupInfoA = _STARTUPINFOA; + TStartupInfoW = record + cb: DWORD; + lpReserved: PWideChar; + lpDesktop: PWideChar; + lpTitle: PWideChar; + dwX: DWORD; + dwY: DWORD; + dwXSize: DWORD; + dwYSize: DWORD; + dwXCountChars: DWORD; + dwYCountChars: DWORD; + dwFillAttribute: DWORD; + dwFlags: DWORD; + wShowWindow: Word; + cbReserved2: Word; + lpReserved2: PByte; + hStdInput: THandle; + hStdOutput: THandle; + hStdError: THandle; + end; + {$ENDIF} + +function CreateProcessW{TNT-ALLOW CreateProcessW}(lpApplicationName: PWideChar; lpCommandLine: PWideChar; + lpProcessAttributes, lpThreadAttributes: PSecurityAttributes; + bInheritHandles: BOOL; dwCreationFlags: DWORD; lpEnvironment: Pointer; + lpCurrentDirectory: PWideChar; const lpStartupInfo: TStartupInfoW; + var lpProcessInformation: TProcessInformation): BOOL; stdcall; external kernel32 name 'CreateProcessW'; + +{$ENDIF} + +{$IFDEF FPC} +type + TCurrencyFmtA = CURRENCYFMT; + TCurrencyFmtW = CURRENCYFMT; + PCurrencyFmtA = ^TCurrencyFmtA; + PCurrencyFmtW = ^TCurrencyFmtW; +{$ENDIF} + +//------------------------------------------------------------------------------------------ + +{TNT-WARN SetWindowText} +{TNT-WARN SetWindowTextA} +{TNT-WARN SetWindowTextW} +function Tnt_SetWindowTextW(hWnd: HWND; lpString: PWideChar): BOOL; + +{TNT-WARN RemoveDirectory} +{TNT-WARN RemoveDirectoryA} +{TNT-WARN RemoveDirectoryW} +function Tnt_RemoveDirectoryW(lpPathName: PWideChar): BOOL; + +{TNT-WARN GetShortPathName} +{TNT-WARN GetShortPathNameA} +{TNT-WARN GetShortPathNameW} +function Tnt_GetShortPathNameW(lpszLongPath: PWideChar; lpszShortPath: PWideChar; + cchBuffer: DWORD): DWORD; + +{TNT-WARN GetFullPathName} +{TNT-WARN GetFullPathNameA} +{TNT-WARN GetFullPathNameW} +function Tnt_GetFullPathNameW(lpFileName: PWideChar; nBufferLength: DWORD; + lpBuffer: PWideChar; var lpFilePart: PWideChar): DWORD; + +{TNT-WARN CreateFile} +{TNT-WARN CreateFileA} +{TNT-WARN CreateFileW} +function Tnt_CreateFileW(lpFileName: PWideChar; dwDesiredAccess, dwShareMode: DWORD; + lpSecurityAttributes: PSecurityAttributes; dwCreationDisposition, dwFlagsAndAttributes: DWORD; + hTemplateFile: THandle): THandle; + +{TNT-WARN FindFirstFile} +{TNT-WARN FindFirstFileA} +{TNT-WARN FindFirstFileW} +function Tnt_FindFirstFileW(lpFileName: PWideChar; var lpFindFileData: TWIN32FindDataW): THandle; + +{TNT-WARN FindNextFile} +{TNT-WARN FindNextFileA} +{TNT-WARN FindNextFileW} +function Tnt_FindNextFileW(hFindFile: THandle; var lpFindFileData: TWIN32FindDataW): BOOL; + +{TNT-WARN GetFileAttributes} +{TNT-WARN GetFileAttributesA} +{TNT-WARN GetFileAttributesW} +function Tnt_GetFileAttributesW(lpFileName: PWideChar): DWORD; + +{TNT-WARN SetFileAttributes} +{TNT-WARN SetFileAttributesA} +{TNT-WARN SetFileAttributesW} +function Tnt_SetFileAttributesW(lpFileName: PWideChar; dwFileAttributes: DWORD): BOOL; + +{TNT-WARN CreateDirectory} +{TNT-WARN CreateDirectoryA} +{TNT-WARN CreateDirectoryW} +function Tnt_CreateDirectoryW(lpPathName: PWideChar; + lpSecurityAttributes: PSecurityAttributes): BOOL; + +{TNT-WARN MoveFile} +{TNT-WARN MoveFileA} +{TNT-WARN MoveFileW} +function Tnt_MoveFileW(lpExistingFileName, lpNewFileName: PWideChar): BOOL; + +{TNT-WARN CopyFile} +{TNT-WARN CopyFileA} +{TNT-WARN CopyFileW} +function Tnt_CopyFileW(lpExistingFileName, lpNewFileName: PWideChar; bFailIfExists: BOOL): BOOL; + +{TNT-WARN DeleteFile} +{TNT-WARN DeleteFileA} +{TNT-WARN DeleteFileW} +function Tnt_DeleteFileW(lpFileName: PWideChar): BOOL; + +{TNT-WARN DrawText} +{TNT-WARN DrawTextA} +{TNT-WARN DrawTextW} +function Tnt_DrawTextW(hDC: HDC; lpString: PWideChar; nCount: Integer; + var lpRect: TRect; uFormat: UINT): Integer; + +{TNT-WARN GetDiskFreeSpace} +{TNT-WARN GetDiskFreeSpaceA} +{TNT-WARN GetDiskFreeSpaceW} +function Tnt_GetDiskFreeSpaceW(lpRootPathName: PWideChar; var lpSectorsPerCluster, + lpBytesPerSector, lpNumberOfFreeClusters, lpTotalNumberOfClusters: DWORD): BOOL; + +{TNT-WARN GetVolumeInformation} +{TNT-WARN GetVolumeInformationA} +{TNT-WARN GetVolumeInformationW} +function Tnt_GetVolumeInformationW(lpRootPathName: PWideChar; lpVolumeNameBuffer: PWideChar; + nVolumeNameSize: DWORD; lpVolumeSerialNumber: PDWORD; + var lpMaximumComponentLength, lpFileSystemFlags: DWORD; lpFileSystemNameBuffer: PWideChar; + nFileSystemNameSize: DWORD): BOOL; + +{TNT-WARN GetModuleFileName} +{TNT-WARN GetModuleFileNameA} +{TNT-WARN GetModuleFileNameW} +function Tnt_GetModuleFileNameW(hModule: HINST; lpFilename: PWideChar; nSize: DWORD): DWORD; + +{TNT-WARN GetTempPath} +{TNT-WARN GetTempPathA} +{TNT-WARN GetTempPathW} +function Tnt_GetTempPathW(nBufferLength: DWORD; lpBuffer: PWideChar): DWORD; + +{TNT-WARN GetTempFileName} +{TNT-WARN GetTempFileNameA} +{TNT-WARN GetTempFileNameW} +function Tnt_GetTempFileNameW(lpPathName, lpPrefixString: PWideChar; uUnique: UINT; + lpTempFileName: PWideChar): UINT; + +{TNT-WARN GetWindowsDirectory} +{TNT-WARN GetWindowsDirectoryA} +{TNT-WARN GetWindowsDirectoryW} +function Tnt_GetWindowsDirectoryW(lpBuffer: PWideChar; uSize: UINT): UINT; + +{TNT-WARN GetSystemDirectory} +{TNT-WARN GetSystemDirectoryA} +{TNT-WARN GetSystemDirectoryW} +function Tnt_GetSystemDirectoryW(lpBuffer: PWideChar; uSize: UINT): UINT; + +{TNT-WARN GetCurrentDirectory} +{TNT-WARN GetCurrentDirectoryA} +{TNT-WARN GetCurrentDirectoryW} +function Tnt_GetCurrentDirectoryW(nBufferLength: DWORD; lpBuffer: PWideChar): DWORD; + +{TNT-WARN SetCurrentDirectory} +{TNT-WARN SetCurrentDirectoryA} +{TNT-WARN SetCurrentDirectoryW} +function Tnt_SetCurrentDirectoryW(lpPathName: PWideChar): BOOL; + +{TNT-WARN GetComputerName} +{TNT-WARN GetComputerNameA} +{TNT-WARN GetComputerNameW} +function Tnt_GetComputerNameW(lpBuffer: PWideChar; var nSize: DWORD): BOOL; + +{TNT-WARN GetUserName} +{TNT-WARN GetUserNameA} +{TNT-WARN GetUserNameW} +function Tnt_GetUserNameW(lpBuffer: PWideChar; var nSize: DWORD): BOOL; + +{TNT-WARN ShellExecute} +{TNT-WARN ShellExecuteA} +{TNT-WARN ShellExecuteW} +function Tnt_ShellExecuteW(hWnd: HWND; Operation, FileName, Parameters, + Directory: PWideChar; ShowCmd: Integer): HINST; + +{TNT-WARN LoadLibrary} +{TNT-WARN LoadLibraryA} +{TNT-WARN LoadLibraryW} +function Tnt_LoadLibraryW(lpLibFileName: PWideChar): HMODULE; + +{TNT-WARN LoadLibraryEx} +{TNT-WARN LoadLibraryExA} +{TNT-WARN LoadLibraryExW} +function Tnt_LoadLibraryExW(lpLibFileName: PWideChar; hFile: THandle; dwFlags: DWORD): HMODULE; + +{TNT-WARN CreateProcess} +{TNT-WARN CreateProcessA} +{TNT-WARN CreateProcessW} +function Tnt_CreateProcessW(lpApplicationName: PWideChar; lpCommandLine: PWideChar; + lpProcessAttributes, lpThreadAttributes: PSecurityAttributes; + bInheritHandles: BOOL; dwCreationFlags: DWORD; lpEnvironment: Pointer; + lpCurrentDirectory: PWideChar; const lpStartupInfo: TStartupInfoW; + var lpProcessInformation: TProcessInformation): BOOL; + +{TNT-WARN GetCurrencyFormat} +{TNT-WARN GetCurrencyFormatA} +{TNT-WARN GetCurrencyFormatW} +function Tnt_GetCurrencyFormatW(Locale: LCID; dwFlags: DWORD; lpValue: PWideChar; + lpFormat: PCurrencyFmtW; lpCurrencyStr: PWideChar; cchCurrency: Integer): Integer; + +{TNT-WARN CompareString} +{TNT-WARN CompareStringA} +{TNT-WARN CompareStringW} +function Tnt_CompareStringW(Locale: LCID; dwCmpFlags: DWORD; lpString1: PWideChar; + cchCount1: Integer; lpString2: PWideChar; cchCount2: Integer): Integer; + +{TNT-WARN CharUpper} +{TNT-WARN CharUpperA} +{TNT-WARN CharUpperW} +function Tnt_CharUpperW(lpsz: PWideChar): PWideChar; + +{TNT-WARN CharUpperBuff} +{TNT-WARN CharUpperBuffA} +{TNT-WARN CharUpperBuffW} +function Tnt_CharUpperBuffW(lpsz: PWideChar; cchLength: DWORD): DWORD; + +{TNT-WARN CharLower} +{TNT-WARN CharLowerA} +{TNT-WARN CharLowerW} +function Tnt_CharLowerW(lpsz: PWideChar): PWideChar; + +{TNT-WARN CharLowerBuff} +{TNT-WARN CharLowerBuffA} +{TNT-WARN CharLowerBuffW} +function Tnt_CharLowerBuffW(lpsz: PWideChar; cchLength: DWORD): DWORD; + +{TNT-WARN GetStringTypeEx} +{TNT-WARN GetStringTypeExA} +{TNT-WARN GetStringTypeExW} +function Tnt_GetStringTypeExW(Locale: LCID; dwInfoType: DWORD; + lpSrcStr: PWideChar; cchSrc: Integer; var lpCharType): BOOL; + +{TNT-WARN LoadString} +{TNT-WARN LoadStringA} +{TNT-WARN LoadStringW} +function Tnt_LoadStringW(hInstance: HINST; uID: UINT; lpBuffer: PWideChar; nBufferMax: Integer): Integer; + +{$IFDEF FPC} +type + TMenuItemInfoW = TMENUITEMINFO; + tagMenuItemINFOW = tagMENUITEMINFO; +{$ENDIF} + +{TNT-WARN InsertMenuItem} +{TNT-WARN InsertMenuItemA} +{TNT-WARN InsertMenuItemW} +function Tnt_InsertMenuItemW(hMenu: HMENU; uItem: DWORD; fByPosition: BOOL; lpmii: tagMenuItemINFOW): BOOL; + +{TNT-WARN ExtractIconEx} +{TNT-WARN ExtractIconExA} +{TNT-WARN ExtractIconExW} +function Tnt_ExtractIconExW(lpszFile: PWideChar; nIconIndex: Integer; + var phiconLarge, phiconSmall: HICON; nIcons: UINT): UINT; + +{TNT-WARN ExtractAssociatedIcon} +{TNT-WARN ExtractAssociatedIconA} +{TNT-WARN ExtractAssociatedIconW} +function Tnt_ExtractAssociatedIconW(hInst: HINST; lpIconPath: PWideChar; + var lpiIcon: Word): HICON; + +{TNT-WARN GetFileVersionInfoSize} +{TNT-WARN GetFileVersionInfoSizeA} +{TNT-WARN GetFileVersionInfoSizeW} +function Tnt_GetFileVersionInfoSizeW(lptstrFilename: PWideChar; var lpdwHandle: DWORD): DWORD; + +{TNT-WARN GetFileVersionInfo} +{TNT-WARN GetFileVersionInfoA} +{TNT-WARN GetFileVersionInfoW} +function Tnt_GetFileVersionInfoW(lptstrFilename: PWideChar; dwHandle, dwLen: DWORD; + lpData: Pointer): BOOL; + +const + VQV_FIXEDFILEINFO = '\'; + VQV_VARFILEINFO_TRANSLATION = '\VarFileInfo\Translation'; + VQV_STRINGFILEINFO = '\StringFileInfo'; + + VER_COMMENTS = 'Comments'; + VER_INTERNALNAME = 'InternalName'; + VER_PRODUCTNAME = 'ProductName'; + VER_COMPANYNAME = 'CompanyName'; + VER_LEGALCOPYRIGHT = 'LegalCopyright'; + VER_PRODUCTVERSION = 'ProductVersion'; + VER_FILEDESCRIPTION = 'FileDescription'; + VER_LEGALTRADEMARKS = 'LegalTrademarks'; + VER_PRIVATEBUILD = 'PrivateBuild'; + VER_FILEVERSION = 'FileVersion'; + VER_ORIGINALFILENAME = 'OriginalFilename'; + VER_SPECIALBUILD = 'SpecialBuild'; + +{TNT-WARN VerQueryValue} +{TNT-WARN VerQueryValueA} +{TNT-WARN VerQueryValueW} +function Tnt_VerQueryValueW(pBlock: Pointer; lpSubBlock: PWideChar; + var lplpBuffer: Pointer; var puLen: UINT): BOOL; + +type +{$IFDEF FPC} + PSHNAMEMAPPINGA = ^SHNAMEMAPPINGA; + SHNAMEMAPPINGA = record + pszOldPath : LPSTR; + pszNewPath : LPSTR; + cchOldPath : longint; + cchNewPath : longint; + end; + + PSHNAMEMAPPINGW = ^SHNAMEMAPPINGW; + SHNAMEMAPPINGW = record + pszOldPath : LPWSTR; + pszNewPath : LPWSTR; + cchOldPath : longint; + cchNewPath : longint; + end; +{$ENDIF} + + TSHNameMappingHeaderA = record + cNumOfMappings: Cardinal; + lpNM: PSHNAMEMAPPINGA; + end; + PSHNameMappingHeaderA = ^TSHNameMappingHeaderA; + + TSHNameMappingHeaderW = record + cNumOfMappings: Cardinal; + lpNM: PSHNAMEMAPPINGW; + end; + PSHNameMappingHeaderW = ^TSHNameMappingHeaderW; + +{TNT-WARN SHFileOperation} +{TNT-WARN SHFileOperationA} +{TNT-WARN SHFileOperationW} // <-- no stub on early Windows 95 +function Tnt_SHFileOperationW(var lpFileOp: TSHFileOpStructW): Integer; + +{TNT-WARN SHFreeNameMappings} +procedure Tnt_SHFreeNameMappings(hNameMappings: THandle); + +{TNT-WARN SHBrowseForFolder} +{TNT-WARN SHBrowseForFolderA} +{TNT-WARN SHBrowseForFolderW} // <-- no stub on early Windows 95 +function Tnt_SHBrowseForFolderW(var lpbi: TBrowseInfoW): PItemIDList; + +{TNT-WARN SHGetPathFromIDList} +{TNT-WARN SHGetPathFromIDListA} +{TNT-WARN SHGetPathFromIDListW} // <-- no stub on early Windows 95 +function Tnt_SHGetPathFromIDListW(pidl: PItemIDList; pszPath: PWideChar): BOOL; + +{TNT-WARN SHGetFileInfo} +{TNT-WARN SHGetFileInfoA} +{TNT-WARN SHGetFileInfoW} // <-- no stub on early Windows 95 +function Tnt_SHGetFileInfoW(pszPath: PWideChar; dwFileAttributes: DWORD; + var psfi: TSHFileInfoW; cbFileInfo, uFlags: UINT): DWORD; + +// ......... introduced ......... +function Tnt_Is_IntResource(ResStr: LPCWSTR): Boolean; + +function LANGIDFROMLCID(lcid: LCID): WORD; +function MAKELANGID(usPrimaryLanguage, usSubLanguage: WORD): WORD; +function MAKELCID(wLanguageID: WORD; wSortID: WORD = SORT_DEFAULT): LCID; +function PRIMARYLANGID(lgid: WORD): WORD; +function SORTIDFROMLCID(lcid: LCID): WORD; +function SUBLANGID(lgid: WORD): WORD; + +implementation + +uses + SysUtils, Math, TntSysUtils, + {$IFDEF COMPILER_9_UP} WideStrUtils, {$ENDIF} TntWideStrUtils; + +function _PAnsiCharWithNil(const S: AnsiString): PAnsiChar; +begin + if S = '' then + Result := nil {Win9x needs nil for some parameters instead of empty strings} + else + Result := PAnsiChar(S); +end; + +function _PWideCharWithNil(const S: WideString): PWideChar; +begin + if S = '' then + Result := nil {Win9x needs nil for some parameters instead of empty strings} + else + Result := PWideChar(S); +end; + +function _WStr(lpString: PWideChar; cchCount: Integer): WideString; +begin + if cchCount = -1 then + Result := lpString + else + Result := Copy(WideString(lpString), 1, cchCount); +end; + +procedure _MakeWideWin32FindData(var WideFindData: TWIN32FindDataW; AnsiFindData: TWIN32FindDataA); +begin + CopyMemory(@WideFindData, @AnsiFindData, + PtrUInt(@WideFindData.cFileName) - PtrUInt(@WideFindData)); + WStrPCopy(WideFindData.cFileName, AnsiFindData.cFileName); + WStrPCopy(WideFindData.cAlternateFileName, AnsiFindData.cAlternateFileName); +end; + +function Tnt_SetWindowTextW(hWnd: HWND; lpString: PWideChar): BOOL; +begin + if Win32PlatformIsUnicode then + Result := SetWindowTextW{TNT-ALLOW SetWindowTextW}(hWnd, lpString) + else + Result := SetWindowTextA{TNT-ALLOW SetWindowTextA}(hWnd, PAnsiChar(AnsiString(lpString))); +end; + +//----------------------------- + +type + TPathLengthResultOption = (poAllowDirectoryMode, poZeroSmallBuff, poExactCopy, poExactCopySubPaths); + TPathLengthResultOptions = set of TPathLengthResultOption; + +procedure _ExactStrCopyW(pDest, pSource: PWideChar; Count: Integer); +var + i: integer; +begin + for i := 1 to Count do begin + pDest^ := pSource^; + Inc(PSource); + Inc(pDest); + end; +end; + +procedure _ExactCopySubPaths(pDest, pSource: PWideChar; Count: Integer); +var + i: integer; + OriginalSource: PWideChar; + PNextSlash: PWideChar; +begin + if Count >= 4 then begin + OriginalSource := pSource; + PNextSlash := WStrScan(pSource, '\'); + for i := 1 to Count - 1 do begin + // determine next path delimiter + if pSource > pNextSlash then begin + PNextSlash := WStrScan(pSource, '\'); + end; + // leave if no more sub paths + if (PNextSlash = nil) + or ((pNextSlash - OriginalSource) >= Count) then begin + exit; + end; + // copy char + pDest^ := pSource^; + Inc(PSource); + Inc(pDest); + end; + end; +end; + +function _HandlePathLengthResult(nBufferLength: DWORD; lpBuffer: PWideChar; const AnsiBuff: AnsiString; Options: TPathLengthResultOptions): Integer; +var + WideBuff: WideString; +begin + WideBuff := AnsiBuff; + if nBufferLength > Cardinal(Length(WideBuff)) then begin + // normal + Result := Length(WideBuff); + WStrLCopy(lpBuffer, PWideChar(WideBuff), nBufferLength); + end else if (poExactCopy in Options) then begin + // exact + Result := nBufferLength; + _ExactStrCopyW(lpBuffer, PWideChar(WideBuff), nBufferLength); + end else begin + // other + if (poAllowDirectoryMode in Options) + and (nBufferLength = Cardinal(Length(WideBuff))) then begin + Result := Length(WideBuff) + 1; + WStrLCopy(lpBuffer, PWideChar(WideBuff), nBufferLength - 1); + end else begin + Result := Length(WideBuff) + 1; + if (nBufferLength > 0) then begin + if (poZeroSmallBuff in Options) then + lpBuffer^ := #0 + else if (poExactCopySubPaths in Options) then + _ExactCopySubPaths(lpBuffer, PWideChar(WideBuff), nBufferLength); + end; + end; + end; +end; + +function _HandleStringLengthResult(nBufferLength: DWORD; lpBuffer: PWideChar; const AnsiBuff: AnsiString; Options: TPathLengthResultOptions): Integer; +var + WideBuff: WideString; +begin + WideBuff := AnsiBuff; + if nBufferLength >= Cardinal(Length(WideBuff)) then begin + // normal + Result := Length(WideBuff); + WStrLCopy(lpBuffer, PWideChar(WideBuff), nBufferLength); + end else if nBufferLength = 0 then + Result := Length(WideBuff) + else + Result := 0; +end; + +//------------------------------------------- + +function Tnt_RemoveDirectoryW(lpPathName: PWideChar): BOOL; +begin + if Win32PlatformIsUnicode then + Result := RemoveDirectoryW{TNT-ALLOW RemoveDirectoryW}(PWideChar(lpPathName)) + else + Result := RemoveDirectoryA{TNT-ALLOW RemoveDirectoryA}(PAnsiChar(AnsiString(lpPathName))); +end; + +function Tnt_GetShortPathNameW(lpszLongPath: PWideChar; lpszShortPath: PWideChar; + cchBuffer: DWORD): DWORD; +var + AnsiBuff: AnsiString; +begin + if Win32PlatformIsUnicode then + Result := GetShortPathNameW{TNT-ALLOW GetShortPathNameW}(lpszLongPath, lpszShortPath, cchBuffer) + else begin + SetLength(AnsiBuff, MAX_PATH * 2); + SetLength(AnsiBuff, GetShortPathNameA{TNT-ALLOW GetShortPathNameA}(PAnsiChar(AnsiString(lpszLongPath)), + PAnsiChar(AnsiBuff), Length(AnsiBuff))); + Result := _HandlePathLengthResult(cchBuffer, lpszShortPath, AnsiBuff, [poExactCopySubPaths]); + end; +end; + +function Tnt_GetFullPathNameW(lpFileName: PWideChar; nBufferLength: DWORD; + lpBuffer: PWideChar; var lpFilePart: PWideChar): DWORD; +var + AnsiBuff: AnsiString; + AnsiFilePart: PAnsiChar; + AnsiLeadingChars: Integer; + WideLeadingChars: Integer; +begin + if Win32PlatformIsUnicode then + Result := GetFullPathNameW{TNT-ALLOW GetFullPathNameW}(lpFileName, nBufferLength, lpBuffer, lpFilePart) + else begin + SetLength(AnsiBuff, MAX_PATH * 2); + SetLength(AnsiBuff, GetFullPathNameA{TNT-ALLOW GetFullPathNameA}(PAnsiChar(AnsiString(lpFileName)), + Length(AnsiBuff), PAnsiChar(AnsiBuff), AnsiFilePart)); + Result := _HandlePathLengthResult(nBufferLength, lpBuffer, AnsiBuff, [poZeroSmallBuff]); + // deal w/ lpFilePart + if (AnsiFilePart = nil) or (nBufferLength < Result) then + lpFilePart := nil + else begin + AnsiLeadingChars := AnsiFilePart - PAnsiChar(AnsiBuff); + WideLeadingChars := Length(WideString(Copy(AnsiBuff, 1, AnsiLeadingChars))); + lpFilePart := lpBuffer + WideLeadingChars; + end; + end; +end; + +function Tnt_CreateFileW(lpFileName: PWideChar; dwDesiredAccess, dwShareMode: DWORD; + lpSecurityAttributes: PSecurityAttributes; dwCreationDisposition, dwFlagsAndAttributes: DWORD; + hTemplateFile: THandle): THandle; +begin + if Win32PlatformIsUnicode then + Result := CreateFileW{TNT-ALLOW CreateFileW}(lpFileName, dwDesiredAccess, dwShareMode, + lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile) + else + Result := CreateFileA{TNT-ALLOW CreateFileA}(PAnsiChar(AnsiString(lpFileName)), dwDesiredAccess, dwShareMode, + lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile) +end; + +function Tnt_FindFirstFileW(lpFileName: PWideChar; var lpFindFileData: TWIN32FindDataW): THandle; +var + Ansi_lpFindFileData: TWIN32FindDataA; +begin + if Win32PlatformIsUnicode then + Result := FindFirstFileW{TNT-ALLOW FindFirstFileW}(lpFileName, lpFindFileData) + else begin + Result := FindFirstFileA{TNT-ALLOW FindFirstFileA}(PAnsiChar(AnsiString(lpFileName)), + Ansi_lpFindFileData); + if Result <> INVALID_HANDLE_VALUE then + _MakeWideWin32FindData(lpFindFileData, Ansi_lpFindFileData); + end; +end; + +function Tnt_FindNextFileW(hFindFile: THandle; var lpFindFileData: TWIN32FindDataW): BOOL; +var + Ansi_lpFindFileData: TWIN32FindDataA; +begin + if Win32PlatformIsUnicode then + Result := FindNextFileW{TNT-ALLOW FindNextFileW}(hFindFile, lpFindFileData) + else begin + Result := FindNextFileA{TNT-ALLOW FindNextFileA}(hFindFile, Ansi_lpFindFileData); + if Result then + _MakeWideWin32FindData(lpFindFileData, Ansi_lpFindFileData); + end; +end; + +function Tnt_GetFileAttributesW(lpFileName: PWideChar): DWORD; +begin + if Win32PlatformIsUnicode then + Result := GetFileAttributesW{TNT-ALLOW GetFileAttributesW}(lpFileName) + else + Result := GetFileAttributesA{TNT-ALLOW GetFileAttributesA}(PAnsiChar(AnsiString(lpFileName))); +end; + +function Tnt_SetFileAttributesW(lpFileName: PWideChar; dwFileAttributes: DWORD): BOOL; +begin + if Win32PlatformIsUnicode then + Result := SetFileAttributesW{TNT-ALLOW SetFileAttributesW}(lpFileName, dwFileAttributes) + else + Result := SetFileAttributesA{TNT-ALLOW SetFileAttributesA}(PAnsiChar(AnsiString(lpFileName)), dwFileAttributes); +end; + +function Tnt_CreateDirectoryW(lpPathName: PWideChar; + lpSecurityAttributes: PSecurityAttributes): BOOL; +begin + if Win32PlatformIsUnicode then + Result := CreateDirectoryW{TNT-ALLOW CreateDirectoryW}(lpPathName, lpSecurityAttributes) + else + Result := CreateDirectoryA{TNT-ALLOW CreateDirectoryA}(PAnsiChar(AnsiString(lpPathName)), lpSecurityAttributes); +end; + +function Tnt_MoveFileW(lpExistingFileName, lpNewFileName: PWideChar): BOOL; +begin + if Win32PlatformIsUnicode then + Result := MoveFileW{TNT-ALLOW MoveFileW}(lpExistingFileName, lpNewFileName) + else + Result := MoveFileA{TNT-ALLOW MoveFileA}(PAnsiChar(AnsiString(lpExistingFileName)), PAnsiChar(AnsiString(lpNewFileName))); +end; + +function Tnt_CopyFileW(lpExistingFileName, lpNewFileName: PWideChar; bFailIfExists: BOOL): BOOL; +begin + if Win32PlatformIsUnicode then + Result := CopyFileW{TNT-ALLOW CopyFileW}(lpExistingFileName, lpNewFileName, bFailIfExists) + else + Result := CopyFileA{TNT-ALLOW CopyFileA}(PAnsiChar(AnsiString(lpExistingFileName)), + PAnsiChar(AnsiString(lpNewFileName)), bFailIfExists); +end; + +function Tnt_DeleteFileW(lpFileName: PWideChar): BOOL; +begin + if Win32PlatformIsUnicode then + Result := DeleteFileW{TNT-ALLOW DeleteFileW}(lpFileName) + else + Result := DeleteFileA{TNT-ALLOW DeleteFileA}(PAnsiChar(AnsiString(lpFileName))); +end; + +function Tnt_DrawTextW(hDC: HDC; lpString: PWideChar; nCount: Integer; + var lpRect: TRect; uFormat: UINT): Integer; +begin + if Win32PlatformIsUnicode then + Result := DrawTextW{TNT-ALLOW DrawTextW}(hDC, lpString, nCount, lpRect, uFormat) + else + Result := DrawTextA{TNT-ALLOW DrawTextA}(hDC, + PAnsiChar(AnsiString(_WStr(lpString, nCount))), -1, lpRect, uFormat); +end; + +function Tnt_GetDiskFreeSpaceW(lpRootPathName: PWideChar; var lpSectorsPerCluster, + lpBytesPerSector, lpNumberOfFreeClusters, lpTotalNumberOfClusters: DWORD): BOOL; +begin + if Win32PlatformIsUnicode then + Result := GetDiskFreeSpaceW{TNT-ALLOW GetDiskFreeSpaceW}(lpRootPathName, + lpSectorsPerCluster, lpBytesPerSector, lpNumberOfFreeClusters, lpTotalNumberOfClusters) + else + Result := GetDiskFreeSpaceA{TNT-ALLOW GetDiskFreeSpaceA}(PAnsiChar(AnsiString(lpRootPathName)), + lpSectorsPerCluster, lpBytesPerSector, lpNumberOfFreeClusters, lpTotalNumberOfClusters) +end; + +function Tnt_GetVolumeInformationW(lpRootPathName: PWideChar; lpVolumeNameBuffer: PWideChar; + nVolumeNameSize: DWORD; lpVolumeSerialNumber: PDWORD; + var lpMaximumComponentLength, lpFileSystemFlags: DWORD; lpFileSystemNameBuffer: PWideChar; + nFileSystemNameSize: DWORD): BOOL; +var + AnsiFileSystemNameBuffer: AnsiString; + AnsiVolumeNameBuffer: AnsiString; + AnsiBuffLen: DWORD; +begin + if Win32PlatformIsUnicode then + Result := GetVolumeInformationW{TNT-ALLOW GetVolumeInformationW}(lpRootPathName, lpVolumeNameBuffer, nVolumeNameSize, lpVolumeSerialNumber, lpMaximumComponentLength, lpFileSystemFlags, lpFileSystemNameBuffer, nFileSystemNameSize) + else begin + SetLength(AnsiVolumeNameBuffer, MAX_COMPUTERNAME_LENGTH + 1); + SetLength(AnsiFileSystemNameBuffer, MAX_COMPUTERNAME_LENGTH + 1); + AnsiBuffLen := Length(AnsiFileSystemNameBuffer); + Result := GetVolumeInformationA{TNT-ALLOW GetVolumeInformationA}(PAnsiChar(AnsiString(lpRootPathName)), PAnsiChar(AnsiVolumeNameBuffer), AnsiBuffLen, lpVolumeSerialNumber, lpMaximumComponentLength, lpFileSystemFlags, PAnsiChar(AnsiFileSystemNameBuffer), AnsiBuffLen); + if Result then begin + SetLength(AnsiFileSystemNameBuffer, AnsiBuffLen); + if (nFileSystemNameSize <= AnsiBuffLen) or (Length(AnsiFileSystemNameBuffer) = 0) then + Result := False + else begin + WStrPLCopy(lpFileSystemNameBuffer, AnsiFileSystemNameBuffer, nFileSystemNameSize); + WStrPLCopy(lpVolumeNameBuffer, AnsiVolumeNameBuffer, nVolumeNameSize); + end; + end; + end; +end; + +function Tnt_GetModuleFileNameW(hModule: HINST; lpFilename: PWideChar; nSize: DWORD): DWORD; +var + AnsiBuff: AnsiString; +begin + if Win32PlatformIsUnicode then + Result := GetModuleFileNameW{TNT-ALLOW GetModuleFileNameW}(hModule, lpFilename, nSize) + else begin + SetLength(AnsiBuff, MAX_PATH); + SetLength(AnsiBuff, GetModuleFileNameA{TNT-ALLOW GetModuleFileNameA}(hModule, PAnsiChar(AnsiBuff), Length(AnsiBuff))); + Result := _HandlePathLengthResult(nSize, lpFilename, AnsiBuff, [poExactCopy]); + end; +end; + +function Tnt_GetTempPathW(nBufferLength: DWORD; lpBuffer: PWideChar): DWORD; +var + AnsiBuff: AnsiString; +begin + if Win32PlatformIsUnicode then + Result := GetTempPathW{TNT-ALLOW GetTempPathW}(nBufferLength, lpBuffer) + else begin + SetLength(AnsiBuff, MAX_PATH); + SetLength(AnsiBuff, GetTempPathA{TNT-ALLOW GetTempPathA}(Length(AnsiBuff), PAnsiChar(AnsiBuff))); + Result := _HandlePathLengthResult(nBufferLength, lpBuffer, AnsiBuff, [poAllowDirectoryMode, poZeroSmallBuff]); + end; +end; + +function Tnt_GetTempFileNameW(lpPathName, lpPrefixString: PWideChar; uUnique: UINT; + lpTempFileName: PWideChar): UINT; +var + AnsiBuff: AnsiString; +begin + if Win32PlatformIsUnicode then + Result := GetTempFileNameW{TNT-ALLOW GetTempFileNameW}(lpPathName, lpPrefixString, uUnique, lpTempFileName) + else begin + SetLength(AnsiBuff, MAX_PATH); + Result := GetTempFileNameA{TNT-ALLOW GetTempFileNameA}(PAnsiChar(AnsiString(lpPathName)), PAnsiChar(lpPrefixString), uUnique, PAnsiChar(AnsiBuff)); + AnsiBuff := PAnsiChar(AnsiBuff); + _HandlePathLengthResult(MAX_PATH, lpTempFileName, AnsiBuff, [poZeroSmallBuff]); + end; +end; + +function Tnt_GetWindowsDirectoryW(lpBuffer: PWideChar; uSize: UINT): UINT; +var + AnsiBuff: AnsiString; +begin + if Win32PlatformIsUnicode then + Result := GetWindowsDirectoryW{TNT-ALLOW GetWindowsDirectoryW}(lpBuffer, uSize) + else begin + SetLength(AnsiBuff, MAX_PATH); + SetLength(AnsiBuff, GetWindowsDirectoryA{TNT-ALLOW GetWindowsDirectoryA}(PAnsiChar(AnsiBuff), Length(AnsiBuff))); + Result := _HandlePathLengthResult(uSize, lpBuffer, AnsiBuff, []); + end; +end; + +function Tnt_GetSystemDirectoryW(lpBuffer: PWideChar; uSize: UINT): UINT; +var + AnsiBuff: AnsiString; +begin + if Win32PlatformIsUnicode then + Result := GetSystemDirectoryW{TNT-ALLOW GetSystemDirectoryW}(lpBuffer, uSize) + else begin + SetLength(AnsiBuff, MAX_PATH); + SetLength(AnsiBuff, GetSystemDirectoryA{TNT-ALLOW GetSystemDirectoryA}(PAnsiChar(AnsiBuff), Length(AnsiBuff))); + Result := _HandlePathLengthResult(uSize, lpBuffer, AnsiBuff, []); + end; +end; + +function Tnt_GetCurrentDirectoryW(nBufferLength: DWORD; lpBuffer: PWideChar): DWORD; +var + AnsiBuff: AnsiString; +begin + if Win32PlatformIsUnicode then + Result := GetCurrentDirectoryW{TNT-ALLOW GetCurrentDirectoryW}(nBufferLength, lpBuffer) + else begin + SetLength(AnsiBuff, MAX_PATH); + SetLength(AnsiBuff, GetCurrentDirectoryA{TNT-ALLOW GetCurrentDirectoryA}(Length(AnsiBuff), PAnsiChar(AnsiBuff))); + Result := _HandlePathLengthResult(nBufferLength, lpBuffer, AnsiBuff, [poAllowDirectoryMode, poZeroSmallBuff]); + end; +end; + +function Tnt_SetCurrentDirectoryW(lpPathName: PWideChar): BOOL; +begin + if Win32PlatformIsUnicode then + Result := SetCurrentDirectoryW{TNT-ALLOW SetCurrentDirectoryW}(lpPathName) + else + Result := SetCurrentDirectoryA{TNT-ALLOW SetCurrentDirectoryA}(PAnsiChar(AnsiString(lpPathName))); +end; + +function Tnt_GetComputerNameW(lpBuffer: PWideChar; var nSize: DWORD): BOOL; +var + AnsiBuff: AnsiString; + AnsiBuffLen: DWORD; +begin + if Win32PlatformIsUnicode then + Result := GetComputerNameW{TNT-ALLOW GetComputerNameW}(lpBuffer, nSize) + else begin + SetLength(AnsiBuff, MAX_COMPUTERNAME_LENGTH + 1); + AnsiBuffLen := Length(AnsiBuff); + Result := GetComputerNameA{TNT-ALLOW GetComputerNameA}(PAnsiChar(AnsiBuff), AnsiBuffLen); + if Result then begin + SetLength(AnsiBuff, AnsiBuffLen); + if (nSize <= AnsiBuffLen) or (Length(AnsiBuff) = 0) then begin + nSize := AnsiBuffLen + 1; + Result := False; + end else begin + WStrPLCopy(lpBuffer, AnsiBuff, nSize); + nSize := WStrLen(lpBuffer); + end; + end; + end; +end; + +function Tnt_GetUserNameW(lpBuffer: PWideChar; var nSize: DWORD): BOOL; +var + AnsiBuff: AnsiString; + AnsiBuffLen: DWORD; +begin + if Win32PlatformIsUnicode then + Result := GetUserNameW{TNT-ALLOW GetUserNameW}(lpBuffer, nSize) + else begin + SetLength(AnsiBuff, 255); + AnsiBuffLen := Length(AnsiBuff); + Result := GetUserNameA{TNT-ALLOW GetUserNameA}(PAnsiChar(AnsiBuff), AnsiBuffLen); + if Result then begin + SetLength(AnsiBuff, AnsiBuffLen); + if (nSize <= AnsiBuffLen) or (Length(AnsiBuff) = 0) then begin + nSize := AnsiBuffLen + 1; + Result := False; + end else begin + WStrPLCopy(lpBuffer, AnsiBuff, nSize); + nSize := WStrLen(lpBuffer); + end; + end; + end; +end; + +function Tnt_ShellExecuteW(hWnd: HWND; Operation, FileName, Parameters, + Directory: PWideChar; ShowCmd: Integer): HINST; +begin + if Win32PlatformIsUnicode then + Result := ShellExecuteW{TNT-ALLOW ShellExecuteW}(hWnd, _PWideCharWithNil(WideString(Operation)), + FileName, Parameters, + Directory, ShowCmd) + else begin + Result := ShellExecuteA{TNT-ALLOW ShellExecuteA}(hWnd, _PAnsiCharWithNil(AnsiString(Operation)), + _PAnsiCharWithNil(AnsiString(FileName)), _PAnsiCharWithNil(AnsiString(Parameters)), + _PAnsiCharWithNil(AnsiString(Directory)), ShowCmd) + end; +end; + +function Tnt_LoadLibraryW(lpLibFileName: PWideChar): HMODULE; +begin + if Win32PlatformIsUnicode then + Result := LoadLibraryW{TNT-ALLOW LoadLibraryW}(lpLibFileName) + else + Result := LoadLibraryA{TNT-ALLOW LoadLibraryA}(PAnsiChar(AnsiString(lpLibFileName))); +end; + +function Tnt_LoadLibraryExW(lpLibFileName: PWideChar; hFile: THandle; dwFlags: DWORD): HMODULE; +begin + if Win32PlatformIsUnicode then + Result := LoadLibraryExW{TNT-ALLOW LoadLibraryExW}(lpLibFileName, hFile, dwFlags) + else + Result := LoadLibraryExA{TNT-ALLOW LoadLibraryExA}(PAnsiChar(AnsiString(lpLibFileName)), hFile, dwFlags); +end; + +function Tnt_CreateProcessW(lpApplicationName: PWideChar; lpCommandLine: PWideChar; + lpProcessAttributes, lpThreadAttributes: PSecurityAttributes; + bInheritHandles: BOOL; dwCreationFlags: DWORD; lpEnvironment: Pointer; + lpCurrentDirectory: PWideChar; const lpStartupInfo: TStartupInfoW; + var lpProcessInformation: TProcessInformation): BOOL; +var + AnsiStartupInfo: TStartupInfoA; +begin + if Win32PlatformIsUnicode then begin + Result := CreateProcessW{TNT-ALLOW CreateProcessW}(lpApplicationName, lpCommandLine, + lpProcessAttributes, lpThreadAttributes, bInheritHandles, dwCreationFlags, lpEnvironment, + lpCurrentDirectory, lpStartupInfo, lpProcessInformation) + end else begin + CopyMemory(@AnsiStartupInfo, @lpStartupInfo, SizeOf(TStartupInfo)); + AnsiStartupInfo.lpReserved := _PAnsiCharWithNil(AnsiString(lpStartupInfo.lpReserved)); + AnsiStartupInfo.lpDesktop := _PAnsiCharWithNil(AnsiString(lpStartupInfo.lpDesktop)); + AnsiStartupInfo.lpTitle := _PAnsiCharWithNil(AnsiString(lpStartupInfo.lpTitle)); + Result := CreateProcessA{TNT-ALLOW CreateProcessA}(_PAnsiCharWithNil(AnsiString(lpApplicationName)), + _PAnsiCharWithNil(AnsiString(lpCommandLine)), + lpProcessAttributes, lpThreadAttributes, bInheritHandles, dwCreationFlags, lpEnvironment, + _PAnsiCharWithNil(AnsiString(lpCurrentDirectory)), AnsiStartupInfo, lpProcessInformation); + end; +end; + +function Tnt_GetCurrencyFormatW(Locale: LCID; dwFlags: DWORD; lpValue: PWideChar; + lpFormat: PCurrencyFmtW; lpCurrencyStr: PWideChar; cchCurrency: Integer): Integer; +const + MAX_ANSI_BUFF_SIZE = 64; // can a currency string actually be larger? +var + AnsiFormat: TCurrencyFmtA; + PAnsiFormat: PCurrencyFmtA; + AnsiBuff: AnsiString; +begin + if Win32PlatformIsUnicode then + Result := GetCurrencyFormatW{TNT-ALLOW GetCurrencyFormatW}(Locale, dwFlags, lpValue, + {$IFNDEF FPC} lpFormat {$ELSE} PCurrencyFmt(lpFormat) {$ENDIF}, + lpCurrencyStr, cchCurrency) + else begin + if lpFormat = nil then + PAnsiFormat := nil + else begin + ZeroMemory(@AnsiFormat, SizeOf(AnsiFormat)); + AnsiFormat.NumDigits := lpFormat.NumDigits; + AnsiFormat.LeadingZero := lpFormat.LeadingZero; + AnsiFormat.Grouping := lpFormat.Grouping; + AnsiFormat.lpDecimalSep := PAnsiChar(AnsiString(lpFormat.lpDecimalSep)); + AnsiFormat.lpThousandSep := PAnsiChar(AnsiString(lpFormat.lpThousandSep)); + AnsiFormat.NegativeOrder := lpFormat.NegativeOrder; + AnsiFormat.PositiveOrder := lpFormat.PositiveOrder; + AnsiFormat.lpCurrencySymbol := PAnsiChar(AnsiString(lpFormat.lpCurrencySymbol)); + PAnsiFormat := @AnsiFormat; + end; + SetLength(AnsiBuff, MAX_ANSI_BUFF_SIZE); + SetLength(AnsiBuff, GetCurrencyFormatA{TNT-ALLOW GetCurrencyFormatA}(Locale, dwFlags, + PAnsiChar(AnsiString(lpValue)), PAnsiFormat, PAnsiChar(AnsiBuff), MAX_ANSI_BUFF_SIZE)); + Result := _HandleStringLengthResult(cchCurrency, lpCurrencyStr, AnsiBuff, []); + end; +end; + +function Tnt_CompareStringW(Locale: LCID; dwCmpFlags: DWORD; lpString1: PWideChar; + cchCount1: Integer; lpString2: PWideChar; cchCount2: Integer): Integer; +var + WideStr1, WideStr2: WideString; + AnsiStr1, AnsiStr2: AnsiString; +begin + if Win32PlatformIsUnicode then + Result := CompareStringW{TNT-ALLOW CompareStringW}(Locale, dwCmpFlags, lpString1, cchCount1, lpString2, cchCount2) + else begin + WideStr1 := _WStr(lpString1, cchCount1); + WideStr2 := _WStr(lpString2, cchCount2); + if (dwCmpFlags = 0) then begin + // binary comparison + if WideStr1 < WideStr2 then + Result := 1 + else if WideStr1 = WideStr2 then + Result := 2 + else + Result := 3; + end else begin + AnsiStr1 := WideStr1; + AnsiStr2 := WideStr2; + Result := CompareStringA{TNT-ALLOW CompareStringA}(Locale, dwCmpFlags, + PAnsiChar(AnsiStr1), -1, PAnsiChar(AnsiStr2), -1); + end; + end; +end; + +function Tnt_CharUpperW(lpsz: PWideChar): PWideChar; +var + AStr: AnsiString; + WStr: WideString; +begin + if Win32PlatformIsUnicode then + Result := CharUpperW{TNT-ALLOW CharUpperW}(lpsz) + else begin + if HiWord(Cardinal(lpsz)) = 0 then begin + // literal char mode + Result := lpsz; + if IsWideCharMappableToAnsi(WideChar(lpsz)) then begin + AStr := WideChar(lpsz); // single character may be more than one byte + CharUpperA{TNT-ALLOW CharUpperA}(PAnsiChar(AStr)); + WStr := AStr; // should always be single wide char + if Length(WStr) = 1 then + Result := PWideChar(WStr[1]); + end + end else begin + // null-terminated string mode + Result := lpsz; + while lpsz^ <> #0 do begin + lpsz^ := WideChar(Tnt_CharUpperW(PWideChar(lpsz^))); + Inc(lpsz); + end; + end; + end; +end; + +function Tnt_CharUpperBuffW(lpsz: PWideChar; cchLength: DWORD): DWORD; +var + i: integer; +begin + if Win32PlatformIsUnicode then + Result := CharUpperBuffW{TNT-ALLOW CharUpperBuffW}(lpsz, cchLength) + else begin + Result := cchLength; + for i := 1 to cchLength do begin + lpsz^ := WideChar(Tnt_CharUpperW(PWideChar(lpsz^))); + Inc(lpsz); + end; + end; +end; + +function Tnt_CharLowerW(lpsz: PWideChar): PWideChar; +var + AStr: AnsiString; + WStr: WideString; +begin + if Win32PlatformIsUnicode then + Result := CharLowerW{TNT-ALLOW CharLowerW}(lpsz) + else begin + if HiWord(Cardinal(lpsz)) = 0 then begin + // literal char mode + Result := lpsz; + if IsWideCharMappableToAnsi(WideChar(lpsz)) then begin + AStr := WideChar(lpsz); // single character may be more than one byte + CharLowerA{TNT-ALLOW CharLowerA}(PAnsiChar(AStr)); + WStr := AStr; // should always be single wide char + if Length(WStr) = 1 then + Result := PWideChar(WStr[1]); + end + end else begin + // null-terminated string mode + Result := lpsz; + while lpsz^ <> #0 do begin + lpsz^ := WideChar(Tnt_CharLowerW(PWideChar(lpsz^))); + Inc(lpsz); + end; + end; + end; +end; + +function Tnt_CharLowerBuffW(lpsz: PWideChar; cchLength: DWORD): DWORD; +var + i: integer; +begin + if Win32PlatformIsUnicode then + Result := CharLowerBuffW{TNT-ALLOW CharLowerBuffW}(lpsz, cchLength) + else begin + Result := cchLength; + for i := 1 to cchLength do begin + lpsz^ := WideChar(Tnt_CharLowerW(PWideChar(lpsz^))); + Inc(lpsz); + end; + end; +end; + +function Tnt_GetStringTypeExW(Locale: LCID; dwInfoType: DWORD; + lpSrcStr: PWideChar; cchSrc: Integer; var lpCharType): BOOL; +var + AStr: AnsiString; +begin + if Win32PlatformIsUnicode then + Result := GetStringTypeExW{TNT-ALLOW GetStringTypeExW}(Locale, dwInfoType, lpSrcStr, cchSrc, lpCharType) + else begin + AStr := _WStr(lpSrcStr, cchSrc); + Result := GetStringTypeExA{TNT-ALLOW GetStringTypeExA}(Locale, dwInfoType, + PAnsiChar(AStr), -1, lpCharType); + end; +end; + +function Win9x_LoadStringW(hInstance: HINST; uID: UINT; lpBuffer: PWideChar; nBufferMax: Integer): Integer; +// This function originated by the WINE Project. +// It was translated to Pascal by Francisco Leong. +// It was further modified by Troy Wolbrink. +var + hmem: HGLOBAL; + hrsrc: THandle; + p: PWideChar; + string_num, i: Integer; + block: Integer; +begin + Result := 0; + // Netscape v3 fix... + if (HIWORD(uID) = $FFFF) then begin + uID := UINT(-(Integer(uID))); + end; + // figure block, string_num + block := ((uID shr 4) and $FFFF) + 1; // bits 4 - 19, mask out bits 20 - 31, inc by 1 + string_num := uID and $000F; + // get handle & pointer to string block + hrsrc := FindResource{TNT-ALLOW FindResource}(hInstance, MAKEINTRESOURCE(block), RT_STRING); + if (hrsrc <> 0) then + begin + hmem := LoadResource(hInstance, hrsrc); + if (hmem <> 0) then + begin + p := LockResource(hmem); + // walk the block to the requested string + for i := 0 to string_num - 1 do begin + p := p + Integer(p^) + 1; + end; + Result := Integer(p^); { p points to the length of string } + Inc(p); { p now points to the actual string } + if (lpBuffer <> nil) and (nBufferMax > 0) then + begin + Result := min(nBufferMax - 1, Result); { max length to copy } + if (Result > 0) then begin + CopyMemory(lpBuffer, p, Result * sizeof(WideChar)); + end; + lpBuffer[Result] := WideChar(0); { null terminate } + end; + end; + end; +end; + +function Tnt_LoadStringW(hInstance: HINST; uID: UINT; lpBuffer: PWideChar; nBufferMax: Integer): Integer; +begin + if Win32PlatformIsUnicode then + Result := Windows.LoadStringW{TNT-ALLOW LoadStringW}(hInstance, uID, lpBuffer, nBufferMax) + else + Result := Win9x_LoadStringW(hInstance, uID, lpBuffer, nBufferMax); +end; + +function Tnt_InsertMenuItemW(hMenu: HMENU; uItem: DWORD; fByPosition: BOOL; lpmii: TMenuItemInfoW): BOOL; +begin + if Win32PlatformIsUnicode then + Result := InsertMenuItemW{TNT-ALLOW InsertMenuItemW}(hMenu, uItem, fByPosition, + {$IFDEF FPC}@{$ENDIF}lpmii) + else begin + TMenuItemInfoA(lpmii).dwTypeData := PAnsiChar(AnsiString(lpmii.dwTypeData)); + Result := InsertMenuItemA{TNT-ALLOW InsertMenuItemA}(hMenu, uItem, fByPosition, + {$IFDEF FPC}@{$ENDIF}TMenuItemInfoA(lpmii)); + end; +end; + +function Tnt_ExtractIconExW(lpszFile: PWideChar; nIconIndex: Integer; + var phiconLarge, phiconSmall: HICON; nIcons: UINT): UINT; +begin + if Win32PlatformIsUnicode then + Result := ExtractIconExW{TNT-ALLOW ExtractIconExW}(lpszFile, + nIconIndex, phiconLarge, phiconSmall, nIcons) + else + Result := ExtractIconExA{TNT-ALLOW ExtractIconExA}(PAnsiChar(AnsiString(lpszFile)), + nIconIndex, phiconLarge, phiconSmall, nIcons); +end; + +function Tnt_ExtractAssociatedIconW(hInst: HINST; lpIconPath: PWideChar; + var lpiIcon: Word): HICON; +begin + if Win32PlatformIsUnicode then + Result := ExtractAssociatedIconW{TNT-ALLOW ExtractAssociatedIconW}(hInst, + lpIconPath, {$IFDEF FPC}@{$ENDIF}lpiIcon) + else + Result := ExtractAssociatedIconA{TNT-ALLOW ExtractAssociatedIconA}(hInst, + PAnsiChar(AnsiString(lpIconPath)), {$IFDEF FPC}@{$ENDIF}lpiIcon) +end; + +function Tnt_GetFileVersionInfoSizeW(lptstrFilename: PWideChar; var lpdwHandle: DWORD): DWORD; +begin + if Win32PlatformIsUnicode then + Result := GetFileVersionInfoSizeW{TNT-ALLOW GetFileVersionInfoSizeW}(lptstrFilename, lpdwHandle) + else + Result := GetFileVersionInfoSizeA{TNT-ALLOW GetFileVersionInfoSizeA}(PAnsiChar(AnsiString(lptstrFilename)), lpdwHandle); +end; + +function Tnt_GetFileVersionInfoW(lptstrFilename: PWideChar; dwHandle, dwLen: DWORD; + lpData: Pointer): BOOL; +begin + if Win32PlatformIsUnicode then + Result := GetFileVersionInfoW{TNT-ALLOW GetFileVersionInfoW}(lptstrFilename, dwHandle, dwLen, lpData) + else + Result := GetFileVersionInfoA{TNT-ALLOW GetFileVersionInfoA}(PAnsiChar(AnsiString(lptstrFilename)), dwHandle, dwLen, lpData); +end; + +var + Last_VerQueryValue_String: WideString; + +function Tnt_VerQueryValueW(pBlock: Pointer; lpSubBlock: PWideChar; + var lplpBuffer: Pointer; var puLen: UINT): BOOL; +var + AnsiBuff: AnsiString; +begin + if Win32PlatformIsUnicode then + Result := VerQueryValueW{TNT-ALLOW VerQueryValueW}(pBlock, lpSubBlock, lplpBuffer, puLen) + else begin + Result := VerQueryValueA{TNT-ALLOW VerQueryValueA}(pBlock, PAnsiChar(AnsiString(lpSubBlock)), lplpBuffer, puLen); + if WideTextPos(VQV_STRINGFILEINFO, lpSubBlock) <> 1 then + else begin + { /StringFileInfo, convert ansi result to unicode } + SetString(AnsiBuff, PAnsiChar(lplpBuffer), puLen); + Last_VerQueryValue_String := AnsiBuff; + lplpBuffer := PWideChar(Last_VerQueryValue_String); + puLen := Length(Last_VerQueryValue_String); + end; + end; +end; + +//--------------------------------------------------------------------------------------- +// Wide functions from Shell32.dll should be loaded dynamically (no stub on early Win95) +//--------------------------------------------------------------------------------------- + +type + TSHFileOperationW = function(var lpFileOp: TSHFileOpStructW): Integer; stdcall; + TSHBrowseForFolderW = function(var lpbi: TBrowseInfoW): PItemIDList; stdcall; + TSHGetPathFromIDListW = function(pidl: PItemIDList; pszPath: PWideChar): BOOL; stdcall; + TSHGetFileInfoW = function(pszPath: PWideChar; dwFileAttributes: DWORD; + var psfi: TSHFileInfoW; cbFileInfo, uFlags: UINT): DWORD; stdcall; + +var + Safe_SHFileOperationW: TSHFileOperationW = nil; + Safe_SHBrowseForFolderW: TSHBrowseForFolderW = nil; + Safe_SHGetPathFromIDListW: TSHGetPathFromIDListW = nil; + Safe_SHGetFileInfoW: TSHGetFileInfoW = nil; + +var Shell32DLL: HModule = 0; + +procedure LoadWideShell32Procs; +begin + if Shell32DLL = 0 then begin + Shell32DLL := WinCheckH(Tnt_LoadLibraryW('shell32.dll')); + Safe_SHFileOperationW := WinCheckP(GetProcAddress(Shell32DLL, 'SHFileOperationW')); + Safe_SHBrowseForFolderW := WinCheckP(GetProcAddress(Shell32DLL, 'SHBrowseForFolderW')); + Safe_SHGetPathFromIDListW := WinCheckP(GetProcAddress(Shell32DLL, 'SHGetPathFromIDListW')); + Safe_SHGetFileInfoW := WinCheckP(GetProcAddress(Shell32DLL, 'SHGetFileInfoW')); + end; +end; + +function Tnt_SHFileOperationW(var lpFileOp: TSHFileOpStructW): Integer; +var + AnsiFileOp: TSHFileOpStructA; + MapCount: Integer; + PAnsiMap: PSHNameMappingA; + PWideMap: PSHNameMappingW; + OldPath: WideString; + NewPath: WideString; + i: integer; +begin + if Win32PlatformIsUnicode then begin + LoadWideShell32Procs; + Result := Safe_SHFileOperationW(lpFileOp); + end else begin + AnsiFileOp := TSHFileOpStructA(lpFileOp); + // convert PChar -> PWideChar + if lpFileOp.pFrom = nil then + AnsiFileOp.pFrom := nil + else + AnsiFileOp.pFrom := PAnsiChar(AnsiString(ExtractStringArrayStr(lpFileOp.pFrom))); + if lpFileOp.pTo = nil then + AnsiFileOp.pTo := nil + else + AnsiFileOp.pTo := PAnsiChar(AnsiString(ExtractStringArrayStr(lpFileOp.pTo))); + AnsiFileOp.lpszProgressTitle := PAnsiChar(AnsiString(lpFileOp.lpszProgressTitle)); + Result := SHFileOperationA{TNT-ALLOW SHFileOperationA}( + {$IFDEF FPC}@{$ENDIF}AnsiFileOp); + // return struct results + lpFileOp.fAnyOperationsAborted := AnsiFileOp.fAnyOperationsAborted; + lpFileOp.hNameMappings := nil; + if (AnsiFileOp.hNameMappings <> nil) + and ((FOF_WANTMAPPINGHANDLE and AnsiFileOp.fFlags) <> 0) then begin + // alloc mem + MapCount := PSHNameMappingHeaderA(AnsiFileOp.hNameMappings).cNumOfMappings; + lpFileOp.hNameMappings := + AllocMem(SizeOf({hNameMappings}Cardinal) + SizeOf(TSHNameMappingW) * MapCount); + PSHNameMappingHeaderW(lpFileOp.hNameMappings).cNumOfMappings := MapCount; + // init pointers + PAnsiMap := PSHNameMappingHeaderA(AnsiFileOp.hNameMappings).lpNM; + PWideMap := PSHNameMappingHeaderW(lpFileOp.hNameMappings).lpNM; + for i := 1 to MapCount do begin + // old path + OldPath := Copy(PAnsiMap.pszOldPath, 1, PAnsiMap.cchOldPath); + PWideMap.pszOldPath := WStrNew(PWideChar(OldPath)); + PWideMap.cchOldPath := WStrLen(PWideMap.pszOldPath); + // new path + NewPath := Copy(PAnsiMap.pszNewPath, 1, PAnsiMap.cchNewPath); + PWideMap.pszNewPath := WStrNew(PWideChar(NewPath)); + PWideMap.cchNewPath := WStrLen(PWideMap.pszNewPath); + // next record + Inc(PAnsiMap); + Inc(PWideMap); + end; + end; + end; +end; + +procedure Tnt_SHFreeNameMappings(hNameMappings: THandle); +var + i: integer; + MapCount: Integer; + PWideMap: PSHNameMappingW; +begin + if Win32PlatformIsUnicode then + SHFreeNameMappings{TNT-ALLOW SHFreeNameMappings}(hNameMappings) + else begin + // free strings + MapCount := PSHNameMappingHeaderW(hNameMappings).cNumOfMappings; + PWideMap := PSHNameMappingHeaderW(hNameMappings).lpNM; + for i := 1 to MapCount do begin + WStrDispose(PWideMap.pszOldPath); + WStrDispose(PWideMap.pszNewPath); + Inc(PWideMap); + end; + // free struct + FreeMem(Pointer(hNameMappings)); + end; +end; + +function Tnt_SHBrowseForFolderW(var lpbi: TBrowseInfoW): PItemIDList; +var + AnsiInfo: TBrowseInfoA; + AnsiBuffer: array[0..MAX_PATH] of AnsiChar; +begin + if Win32PlatformIsUnicode then begin + LoadWideShell32Procs; + Result := Safe_SHBrowseForFolderW(lpbi); + end else begin + AnsiInfo := TBrowseInfoA(lpbi); + AnsiInfo.lpszTitle := PAnsiChar(AnsiString(lpbi.lpszTitle)); + if lpbi.pszDisplayName <> nil then + AnsiInfo.pszDisplayName := AnsiBuffer; + Result := SHBrowseForFolderA{TNT-ALLOW SHBrowseForFolderA}( + {$IFDEF FPC}@{$ENDIF}AnsiInfo); + if lpbi.pszDisplayName <> nil then + WStrPCopy(lpbi.pszDisplayName, AnsiInfo.pszDisplayName); + lpbi.iImage := AnsiInfo.iImage; + end; +end; + +function Tnt_SHGetPathFromIDListW(pidl: PItemIDList; pszPath: PWideChar): BOOL; +var + AnsiPath: AnsiString; +begin + if Win32PlatformIsUnicode then begin + LoadWideShell32Procs; + Result := Safe_SHGetPathFromIDListW(pidl, pszPath); + end else begin + SetLength(AnsiPath, MAX_PATH); + Result := SHGetPathFromIDListA{TNT-ALLOW SHGetPathFromIDListA}(pidl, PAnsiChar(AnsiPath)); + if Result then + WStrPCopy(pszPath, PAnsiChar(AnsiPath)) + end; +end; + +function Tnt_SHGetFileInfoW(pszPath: PWideChar; dwFileAttributes: DWORD; + var psfi: TSHFileInfoW; cbFileInfo, uFlags: UINT): DWORD; +var + SHFileInfoA: TSHFileInfoA; +begin + if Win32PlatformIsUnicode then begin + LoadWideShell32Procs; + Result := Safe_SHGetFileInfoW(pszPath, dwFileAttributes, psfi, cbFileInfo, uFlags) + end else begin + Result := SHGetFileInfoA{TNT-ALLOW SHGetFileInfoA}(PAnsiChar(AnsiString(pszPath)), + dwFileAttributes, SHFileInfoA, SizeOf(TSHFileInfoA), uFlags); + // update pfsi... + ZeroMemory(@psfi, SizeOf(TSHFileInfoW)); + psfi.hIcon := SHFileInfoA.hIcon; + psfi.iIcon := SHFileInfoA.iIcon; + psfi.dwAttributes := SHFileInfoA.dwAttributes; + WStrPLCopy(psfi.szDisplayName, SHFileInfoA.szDisplayName, MAX_PATH); + WStrPLCopy(psfi.szTypeName, SHFileInfoA.szTypeName, 80); + end; +end; + + +function Tnt_Is_IntResource(ResStr: LPCWSTR): Boolean; +begin + Result := HiWord(Cardinal(ResStr)) = 0; +end; + +function LANGIDFROMLCID(lcid: LCID): WORD; +begin + Result := LoWord(lcid); +end; + +function MAKELANGID(usPrimaryLanguage, usSubLanguage: WORD): WORD; +begin + Result := (usSubLanguage shl 10) or usPrimaryLanguage; +end; + +function MAKELCID(wLanguageID: WORD; wSortID: WORD = SORT_DEFAULT): LCID; +begin + Result := MakeLong(wLanguageID, wSortID); +end; + +function PRIMARYLANGID(lgid: WORD): WORD; +begin + Result := lgid and $03FF; +end; + +function SORTIDFROMLCID(lcid: LCID): WORD; +begin + Result := HiWord(lcid); +end; + +function SUBLANGID(lgid: WORD): WORD; +begin + Result := lgid shr 10; +end; + +initialization + +finalization + if Shell32DLL <> 0 then + FreeLibrary(Shell32DLL); + +end. diff --git a/Lua/src/lib/collections/CollArray.pas b/Lua/src/lib/collections/CollArray.pas new file mode 100644 index 00000000..a10ba905 --- /dev/null +++ b/Lua/src/lib/collections/CollArray.pas @@ -0,0 +1,183 @@ +unit CollArray; + +(***************************************************************************** + * Copyright 2003 by Matthew Greet + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * This library 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 Lesser General Public License for more + * details. (http://opensource.org/licenses/lgpl-license.php) + * + * See http://www.warmachine.u-net.com/delphi_collections for updates and downloads. + * + * $Version: v1.0.3 $ + * $Revision: 1.2 $ + * $Log: D:\QVCS Repositories\Delphi Collections\CollArray.qbt $ + * + * Colllection implementations based on arrays. + * + * Revision 1.2 by: Matthew Greet Rev date: 12/06/04 20:02:16 + * Capacity property. + * + * Revision 1.1 by: Matthew Greet Rev date: 06/04/03 10:30:36 + * Size property dropped. + * Unused abstract functions still implemented. + * + * Revision 1.0 by: Matthew Greet Rev date: 01/03/03 10:50:02 + * Initial revision. + * + * FPC compatibility fixes by: UltraStar Deluxe Team + * + * $Endlog$ + *****************************************************************************) + +{$IFDEF FPC} + {$MODE Delphi}{$H+} +{$ENDIF} + +interface + +uses + Collections; + +type + TArray = class(TAbstractList) + private + FArray: array of ICollectable; + protected + function TrueGetItem(Index: Integer): ICollectable; override; + procedure TrueSetItem(Index: Integer; const Value: ICollectable); override; + procedure TrueAppend(const Item: ICollectable); override; + procedure TrueClear; override; + function TrueDelete(Index: Integer): ICollectable; override; + procedure TrueInsert(Index: Integer; const Item: ICollectable); override; + public + constructor Create(NaturalItemsOnly: Boolean); override; + constructor Create(const ItemArray: array of ICollectable; NaturalItemsOnly: Boolean = false); override; + constructor Create(Size: Integer; NaturalItemsOnly: Boolean = false); overload; virtual; + constructor Create(const Collection: ICollection); override; + destructor Destroy; override; + function GetCapacity: Integer; override; + procedure SetCapacity(Value: Integer); override; + function GetFixedSize: Boolean; override; + function GetSize: Integer; override; + end; + +implementation + +constructor TArray.Create(NaturalItemsOnly: Boolean); +begin + Create(0, NaturalItemsOnly); +end; + +constructor TArray.Create(Size: Integer; NaturalItemsOnly: Boolean = false); +begin + inherited Create(NaturalItemsOnly); + SetLength(FArray, Size); +end; + +constructor TArray.Create(const ItemArray: array of ICollectable; NaturalItemsOnly: Boolean); +var + Item: ICollectable; + ItemError: TCollectionError; + I: Integer; +begin + inherited Create(ItemArray, NaturalItemsOnly); + SetLength(FArray, Length(ItemArray)); + for I := Low(ItemArray) to High(ItemArray) do + begin + Item := ItemArray[I]; + ItemError := ItemAllowed(Item); + if ItemError <> ceOK then + begin + CollectionError(ItemError); + end + else + Items[I] := Item; + end; +end; + +constructor TArray.Create(const Collection: ICollection); +var + Iterator: IIterator; + I: Integer; +begin + inherited Create(Collection); + SetLength(FArray, Collection.GetSize); + Iterator := Collection.GetIterator; + I := 0; + while not Iterator.EOF do + begin + Items[I] := Iterator.CurrentItem; + Inc(I); + Iterator.Next; + end; +end; + +destructor TArray.Destroy; +var + I: Integer; +begin + // Delete interface references to all items + for I := Low(FArray) to High(FArray) do + begin + FArray[I] := nil; + end; + inherited Destroy; +end; + +function TArray.TrueGetItem(Index: Integer): ICollectable; +begin + Result := FArray[Index]; +end; + +procedure TArray.TrueSetItem(Index: Integer; const Value: ICollectable); +begin + FArray[Index] := Value; +end; + +procedure TArray.TrueAppend(const Item: ICollectable); +begin + // Ignored as collection is fixed size +end; + +procedure TArray.TrueClear; +begin + // Ignored as collection is fixed size +end; + +function TArray.TrueDelete(Index: Integer): ICollectable; +begin + // Ignored as collection is fixed size +end; + +procedure TArray.TrueInsert(Index: Integer; const Item: ICollectable); +begin + // Ignored as collection is fixed size +end; + +function TArray.GetCapacity: Integer; +begin + Result := Size; +end; + +procedure TArray.SetCapacity(Value: Integer); +begin + // Ignored +end; + +function TArray.GetFixedSize: Boolean; +begin + Result := true; +end; + +function TArray.GetSize: Integer; +begin + Result := Length(FArray); +end; + +end. diff --git a/Lua/src/lib/collections/CollHash.pas b/Lua/src/lib/collections/CollHash.pas new file mode 100644 index 00000000..796fc740 --- /dev/null +++ b/Lua/src/lib/collections/CollHash.pas @@ -0,0 +1,1497 @@ +unit CollHash; + +(***************************************************************************** + * Copyright 2003 by Matthew Greet + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * This library 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 Lesser General Public License for more + * details. (http://opensource.org/licenses/lgpl-license.php) + * + * See http://www.warmachine.u-net.com/delphi_collections for updates and downloads. + * + * $Version: v1.0.3 $ + * $Revision: 1.1.1.2 $ + * $Log: D:\QVCS Repositories\Delphi Collections\CollHash.qbt $ + * + * Collection implementations based on hash tables. + * + * Revision 1.1.1.2 by: Matthew Greet Rev date: 12/06/04 20:04:30 + * Capacity property. + * + * Revision 1.1.1.1 by: Matthew Greet Rev date: 24/10/03 16:48:16 + * v1.0 branch. + * + * Revision 1.1 by: Matthew Greet Rev date: 06/04/03 10:40:16 + * Added integer map and string map versions. + * THashSet uses its own implementation, not THashMap. + * DefaulMaxLoadFactor changed. + * + * Revision 1.0 by: Matthew Greet Rev date: 01/03/03 10:50:02 + * Initial revision. + * + * FPC compatibility fixes by: UltraStar Deluxe Team + * + * $Endlog$ + *****************************************************************************) + +{$IFDEF FPC} + {$MODE Delphi}{$H+} +{$ENDIF} + +interface + +uses + Classes, Math, + Collections; + +const + DefaultTableSize = 100; + MaxLoadFactorMin = 0.01; // Minimum allowed value for MaxLoadFactor property. + DefaultMaxLoadFactor = 5.0; + +type + THashMap = class(TAbstractMap) + private + FArray: TListArray; + FCapacity: Integer; + FMaxLoadFactor: Double; + FSize: Integer; + FTableSize: Integer; + protected + function GetAssociationIterator: IMapIterator; override; + procedure SetMaxLoadFactor(Value: Double); virtual; + procedure SetTableSize(Value: Integer); virtual; + procedure ChangeCapacity(Value: TListArray); virtual; + procedure CheckLoadFactor(AlwaysChangeCapacity: Boolean); virtual; + function GetHash(const Key: ICollectable): Integer; virtual; + function GetKeyPosition(const Key: ICollectable): TCollectionPosition; override; + procedure Rehash; + procedure TrueClear; override; + function TrueGet(Position: TCollectionPosition): IAssociation; override; + function TruePut(Position: TCollectionPosition; const Association: IAssociation): IAssociation; override; + function TrueRemove2(Position: TCollectionPosition): IAssociation; override; + public + constructor Create(NaturalItemsOnly: Boolean; NaturalKeysOnly: Boolean); override; + destructor Destroy; override; + class function GetAlwaysNaturalKeys: Boolean; override; + function GetCapacity: Integer; override; + procedure SetCapacity(Value: Integer); override; + function GetNaturalKeyIID: TGUID; override; + function GetSize: Integer; override; + property MaxLoadFactor: Double read FMaxLoadFactor write SetMaxLoadFactor; + property TableSize: Integer read FTableSize write SetTableSize; + end; + + THashSet = class(TAbstractSet) + private + FArray: TListArray; + FCapacity: Integer; + FMaxLoadFactor: Double; + FSize: Integer; + FTableSize: Integer; + protected + procedure SetMaxLoadFactor(Value: Double); virtual; + procedure SetTableSize(Value: Integer); virtual; + procedure ChangeCapacity(Value: TListArray); virtual; + procedure CheckLoadFactor(AlwaysChangeCapacity: Boolean); virtual; + function GetHash(const Item: ICollectable): Integer; virtual; + function GetPosition(const Item: ICollectable): TCollectionPosition; override; + procedure Rehash; + procedure TrueAdd2(Position: TCollectionPosition; const Item: ICollectable); override; + procedure TrueClear; override; + function TrueGet(Position: TCollectionPosition): ICollectable; override; + procedure TrueRemove2(Position: TCollectionPosition); override; + public + constructor Create(NaturalItemsOnly: Boolean); override; + destructor Destroy; override; + class function GetAlwaysNaturalItems: Boolean; override; + function GetCapacity: Integer; override; + procedure SetCapacity(Value: Integer); override; + function GetIterator: IIterator; override; + function GetNaturalItemIID: TGUID; override; + function GetSize: Integer; override; + property MaxLoadFactor: Double read FMaxLoadFactor write SetMaxLoadFactor; + property TableSize: Integer read FTableSize write SetTableSize; + end; + + THashIntegerMap = class(TAbstractIntegerMap) + private + FArray: TListArray; + FCapacity: Integer; + FMaxLoadFactor: Double; + FSize: Integer; + FTableSize: Integer; + protected + function GetAssociationIterator: IIntegerMapIterator; override; + procedure SetMaxLoadFactor(Value: Double); virtual; + procedure SetTableSize(Value: Integer); virtual; + procedure ChangeCapacity(Value: TListArray); virtual; + procedure CheckLoadFactor(AlwaysChangeCapacity: Boolean); virtual; + function GetHash(const Key: Integer): Integer; virtual; + function GetKeyPosition(const Key: Integer): TCollectionPosition; override; + procedure Rehash; + procedure TrueClear; override; + function TrueGet(Position: TCollectionPosition): IIntegerAssociation; override; + function TruePut(Position: TCollectionPosition; const Association: IIntegerAssociation): IIntegerAssociation; override; + function TrueRemove2(Position: TCollectionPosition): IIntegerAssociation; override; + public + constructor Create; override; + constructor Create(NaturalItemsOnly: Boolean); override; + constructor Create(NaturalItemsOnly: Boolean; TableSize: Integer; MaxLoadFactor: Double = DefaultMaxLoadFactor); overload; virtual; + destructor Destroy; override; + function GetCapacity: Integer; override; + procedure SetCapacity(Value: Integer); override; + function GetSize: Integer; override; + property MaxLoadFactor: Double read FMaxLoadFactor write SetMaxLoadFactor; + property TableSize: Integer read FTableSize write SetTableSize; + end; + + THashStringMap = class(TAbstractStringMap) + private + FArray: TListArray; + FCapacity: Integer; + FMaxLoadFactor: Double; + FSize: Integer; + FTableSize: Integer; + protected + function GetAssociationIterator: IStringMapIterator; override; + procedure SetMaxLoadFactor(Value: Double); virtual; + procedure SetTableSize(Value: Integer); virtual; + procedure ChangeCapacity(Value: TListArray); virtual; + procedure CheckLoadFactor(AlwaysChangeCapacity: Boolean); virtual; + function GetHash(const Key: String): Integer; virtual; + function GetKeyPosition(const Key: String): TCollectionPosition; override; + procedure Rehash; + procedure TrueClear; override; + function TrueGet(Position: TCollectionPosition): IStringAssociation; override; + function TruePut(Position: TCollectionPosition; const Association: IStringAssociation): IStringAssociation; override; + function TrueRemove2(Position: TCollectionPosition): IStringAssociation; override; + public + constructor Create; override; + constructor Create(NaturalItemsOnly: Boolean); override; + constructor Create(NaturalItemsOnly: Boolean; TableSize: Integer; MaxLoadFactor: Double = DefaultMaxLoadFactor); overload; virtual; + destructor Destroy; override; + function GetCapacity: Integer; override; + procedure SetCapacity(Value: Integer); override; + function GetSize: Integer; override; + property MaxLoadFactor: Double read FMaxLoadFactor write SetMaxLoadFactor; + property TableSize: Integer read FTableSize write SetTableSize; + end; + +implementation + +const + (* (sqrt(5) - 1)/2 + See Introduction to Algorithms in Pascal, 1995, by Thomas W. Parsons, + published by John Wiley & Sons, Inc, ISBN 0-471-11600-9 + *) + HashFactor = 0.618033988749894848204586834365638; + +type + THashIterator = class(TAbstractIterator) + private + FHashSet: THashSet; + FHash: Integer; + FChainIndex: Integer; + protected + constructor Create(HashSet: THashSet); + function TrueFirst: ICollectable; override; + function TrueNext: ICollectable; override; + procedure TrueRemove; override; + end; + + THashAssociationIterator = class(TAbstractAssociationIterator) + private + FHashMap: THashMap; + FHash: Integer; + FChainIndex: Integer; + protected + constructor Create(HashMap: THashMap); + function TrueFirst: IAssociation; override; + function TrueNext: IAssociation; override; + procedure TrueRemove; override; + end; + + THashIntegerIterator = class(TAbstractIntegerAssociationIterator) + private + FHashIntegerMap: THashIntegerMap; + FHash: Integer; + FChainIndex: Integer; + protected + constructor Create(HashIntegerMap: THashIntegerMap); + function TrueFirst: IIntegerAssociation; override; + function TrueNext: IIntegerAssociation; override; + procedure TrueRemove; override; + end; + + THashStringIterator = class(TAbstractStringAssociationIterator) + private + FHashStringMap: THashStringMap; + FHash: Integer; + FChainIndex: Integer; + protected + constructor Create(HashStringMap: THashStringMap); + function TrueFirst: IStringAssociation; override; + function TrueNext: IStringAssociation; override; + procedure TrueRemove; override; + end; + + THashPosition = class(TCollectionPosition) + private + FChain: TList; + FIndex: Integer; + public + constructor Create(Found: Boolean; Chain: TList; Index: Integer); + property Chain: TList read FChain; + property Index: Integer read FIndex; + end; + +{ THashMap } +constructor THashMap.Create(NaturalItemsOnly: Boolean; NaturalKeysOnly: Boolean); +var + I: Integer; +begin + // Force use of natural keys + inherited Create(NaturalItemsOnly, true); + FTableSize := DefaultTableSize; + FMaxLoadFactor := DefaultMaxLoadFactor; + SetLength(FArray, FTableSize); + for I := Low(FArray) to High(FArray) do + FArray[I] := TList.Create; + FCapacity := 0; + FSize := 0; + ChangeCapacity(FArray); +end; + +destructor THashMap.Destroy; +var + I: Integer; +begin + for I := Low(FArray) to High(FArray) do + FArray[I].Free; + FArray := nil; + inherited Destroy; +end; + +class function THashMap.GetAlwaysNaturalKeys: Boolean; +begin + Result := true; +end; + +function THashMap.GetNaturalKeyIID: TGUID; +begin + Result := HashableIID; +end; + +function THashMap.GetAssociationIterator: IMapIterator; +begin + Result := THashAssociationIterator.Create(Self); +end; + +procedure THashMap.SetTableSize(Value: Integer); +begin + if (FTableSize <> Value) and (Value >= 1) then + begin + FTableSize := Value; + Rehash; + end; +end; + +procedure THashMap.SetMaxLoadFactor(Value: Double); +begin + if (FMaxLoadFactor <> Value) and (Value >= MaxLoadFactorMin) then + begin + FMaxLoadFactor := Value; + CheckLoadFactor(false); + end; +end; + +procedure THashMap.ChangeCapacity(Value: TListArray); +var + Chain: TList; + I, Total, ChainCapacity: Integer; +begin + if FCapacity mod FTableSize = 0 then + ChainCapacity := Trunc(FCapacity / FTableSize) + else + ChainCapacity := Trunc(FCapacity / FTableSize) + 1; + Total := 0; + for I := Low(Value) to High(Value) do + begin + Chain := Value[I]; + Chain.Capacity := ChainCapacity; + Total := Total + Chain.Capacity; + end; + FCapacity := Total; +end; + +procedure THashMap.CheckLoadFactor(AlwaysChangeCapacity: Boolean); +var + LoadFactor: Double; +begin + LoadFactor := Capacity / TableSize; + if LoadFactor > MaxLoadFactor then + TableSize := Trunc(Capacity / Max(MaxLoadFactor, MaxLoadFactorMin)) + else if AlwaysChangeCapacity then + ChangeCapacity(FArray); +end; + +function THashMap.GetHash(const Key: ICollectable): Integer; +var + Hashable: IHashable; + HashCode: Cardinal; +begin + Key.QueryInterface(IHashable, Hashable); + HashCode := Hashable.HashCode; + Result := Trunc(Frac(HashCode * HashFactor) * TableSize); +end; + +function THashMap.GetKeyPosition(const Key: ICollectable): TCollectionPosition; +var + Chain: TList; + I: Integer; + Success: Boolean; +begin + Chain := FArray[GetHash(Key)]; + Success := false; + for I := 0 to Chain.Count - 1 do + begin + Success := KeyComparator.Equals(Key, IAssociation(Chain[I]).GetKey); + if Success then + Break; + end; + Result := THashPosition.Create(Success, Chain, I); +end; + +procedure THashMap.Rehash; +var + NewArray: TListArray; + OldChain, NewChain: TList; + Association: IAssociation; + Total: Integer; + I, J: Integer; + Hash: Integer; +begin + // Create new chains + SetLength(NewArray, TableSize); + for I := Low(NewArray) to High(NewArray) do + begin + NewChain := TList.Create; + NewArray[I] := NewChain; + end; + ChangeCapacity(NewArray); + + // Transfer from old chains to new and drop old + for I := Low(FArray) to High(FArray) do + begin + OldChain := FArray[I]; + for J := 0 to OldChain.Count - 1 do + begin + Association := IAssociation(OldChain[J]); + Hash := GetHash(Association.GetKey); + NewArray[Hash].Add(Pointer(Association)); + end; + OldChain.Free; + end; + FArray := NewArray; + + // Find actual, new capacity + Total := 0; + for I := Low(FArray) to High(FArray) do + begin + NewChain := FArray[I]; + Total := Total + NewChain.Capacity; + end; + FCapacity := Total; +end; + +procedure THashMap.TrueClear; +var + Association: IAssociation; + Chain: TList; + I, J: Integer; +begin + for I := Low(FArray) to High(FArray) do + begin + Chain := FArray[I]; + for J := 0 to Chain.Count - 1 do + begin + Association := IAssociation(Chain[J]); + Chain[J] := nil; + // Storing interface reference as a pointer does not update reference + // count automatically, so this must be done manually + Association._Release; + end; + Chain.Clear; + end; + FSize := 0; +end; + +function THashMap.TrueGet(Position: TCollectionPosition): IAssociation; +var + HashPosition: THashPosition; +begin + HashPosition := THashPosition(Position); + Result := IAssociation(HashPosition.Chain.Items[HashPosition.Index]); +end; + +function THashMap.TruePut(Position: TCollectionPosition; const Association: IAssociation): IAssociation; +var + HashPosition: THashPosition; + OldAssociation: IAssociation; +begin + HashPosition := THashPosition(Position); + if HashPosition.Found then + begin + OldAssociation := IAssociation(HashPosition.Chain.Items[HashPosition.Index]); + HashPosition.Chain.Items[HashPosition.Index] := Pointer(Association); + Result := OldAssociation; + // Storing interface reference as a pointer does not update reference + // count automatically, so this must be done manually + Association._AddRef; + OldAssociation._Release; + end + else + begin + HashPosition.Chain.Add(Pointer(Association)); + Inc(FSize); + Result := nil; + // Storing interface reference as a pointer does not update reference + // count automatically, so this must be done manually + Association._AddRef; + end; +end; + +function THashMap.TrueRemove2(Position: TCollectionPosition): IAssociation; +var + Association: IAssociation; + HashPosition: THashPosition; +begin + HashPosition := THashPosition(Position); + Association := IAssociation(TrueGet(Position)); + HashPosition.Chain.Delete(HashPosition.Index); + Dec(FSize); + Result := Association; + // Storing interface reference as a pointer does not update reference + // count automatically, so this must be done manually + Association._Release; +end; + +function THashMap.GetCapacity; +begin + Result := FCapacity; +end; + +procedure THashMap.SetCapacity(Value: Integer); +begin + FCapacity := Value; + CheckLoadFactor(true); +end; + +function THashMap.GetSize: Integer; +begin + Result := FSize; +end; + +{ THashSet } +constructor THashSet.Create(NaturalItemsOnly: Boolean); +var + I: Integer; +begin + // Force use of natural items + inherited Create(true); + FTableSize := DefaultTableSize; + FMaxLoadFactor := DefaultMaxLoadFactor; + SetLength(FArray, FTableSize); + for I := Low(FArray) to High(FArray) do + FArray[I] := TList.Create; + FSize := 0; +end; + +destructor THashSet.Destroy; +var + I: Integer; +begin + for I := Low(FArray) to High(FArray) do + FArray[I].Free; + FArray := nil; + inherited Destroy; +end; + +procedure THashSet.SetTableSize(Value: Integer); +begin + if (FTableSize <> Value) and (Value >= 1) then + begin + FTableSize := Value; + Rehash; + end; +end; + +procedure THashSet.SetMaxLoadFactor(Value: Double); +begin + if (FMaxLoadFactor <> Value) and (Value >= MaxLoadFactorMin) then + begin + FMaxLoadFactor := Value; + CheckLoadFactor(false); + end; +end; + +procedure THashSet.ChangeCapacity(Value: TListArray); +var + Chain: TList; + I, Total, ChainCapacity: Integer; +begin + if FCapacity mod FTableSize = 0 then + ChainCapacity := Trunc(FCapacity / FTableSize) + else + ChainCapacity := Trunc(FCapacity / FTableSize) + 1; + Total := 0; + for I := Low(Value) to High(Value) do + begin + Chain := Value[I]; + Chain.Capacity := ChainCapacity; + Total := Total + Chain.Capacity; + end; + FCapacity := Total; +end; + +procedure THashSet.CheckLoadFactor(AlwaysChangeCapacity: Boolean); +var + LoadFactor: Double; +begin + LoadFactor := Capacity / TableSize; + if LoadFactor > MaxLoadFactor then + TableSize := Trunc(Capacity / Max(MaxLoadFactor, MaxLoadFactorMin)) + else if AlwaysChangeCapacity then + ChangeCapacity(FArray); +end; + +function THashSet.GetHash(const Item: ICollectable): Integer; +var + Hashable: IHashable; + HashCode: Cardinal; +begin + Item.QueryInterface(IHashable, Hashable); + HashCode := Hashable.HashCode; + Result := Trunc(Frac(HashCode * HashFactor) * TableSize); +end; + +function THashSet.GetPosition(const Item: ICollectable): TCollectionPosition; +var + Chain: TList; + I: Integer; + Success: Boolean; +begin + Chain := FArray[GetHash(Item)]; + Success := false; + for I := 0 to Chain.Count - 1 do + begin + Success := Comparator.Equals(Item, ICollectable(Chain[I])); + if Success then + Break; + end; + Result := THashPosition.Create(Success, Chain, I); +end; + +procedure THashSet.Rehash; +var + NewArray: TListArray; + OldChain, NewChain: TList; + Item: ICollectable; + Total: Integer; + I, J: Integer; + Hash: Integer; +begin + // Create new chains + SetLength(NewArray, TableSize); + for I := Low(NewArray) to High(NewArray) do + begin + NewChain := TList.Create; + NewArray[I] := NewChain; + end; + ChangeCapacity(NewArray); + + // Transfer from old chains to new and drop old + for I := Low(FArray) to High(FArray) do + begin + OldChain := FArray[I]; + for J := 0 to OldChain.Count - 1 do + begin + Item := ICollectable(OldChain[J]); + Hash := GetHash(Item); + NewArray[Hash].Add(Pointer(Item)); + end; + OldChain.Free; + end; + FArray := NewArray; + + // Find actual, new capacity + Total := 0; + for I := Low(FArray) to High(FArray) do + begin + NewChain := FArray[I]; + Total := Total + NewChain.Capacity; + end; + FCapacity := Total; +end; + +procedure THashSet.TrueAdd2(Position: TCollectionPosition; const Item: ICollectable); +var + HashPosition: THashPosition; +begin + HashPosition := THashPosition(Position); + HashPosition.Chain.Add(Pointer(Item)); + Inc(FSize); + // Storing interface reference as a pointer does not update reference + // count automatically, so this must be done manually + Item._AddRef; +end; + +procedure THashSet.TrueClear; +var + Item: ICollectable; + Chain: TList; + I, J: Integer; +begin + for I := Low(FArray) to High(FArray) do + begin + Chain := FArray[I]; + for J := 0 to Chain.Count - 1 do + begin + Item := ICollectable(Chain[J]); + Chain[J] := nil; + // Storing interface reference as a pointer does not update reference + // count automatically, so this must be done manually + Item._Release; + end; + Chain.Clear; + end; + FSize := 0; +end; + +function THashSet.TrueGet(Position: TCollectionPosition): ICollectable; +var + HashPosition: THashPosition; +begin + HashPosition := THashPosition(Position); + Result := ICollectable(HashPosition.Chain.Items[HashPosition.Index]); +end; + +procedure THashSet.TrueRemove2(Position: TCollectionPosition); +var + Item: ICollectable; + HashPosition: THashPosition; +begin + HashPosition := THashPosition(Position); + Item := TrueGet(Position); + HashPosition.Chain.Delete(HashPosition.Index); + Dec(FSize); + // Storing interface reference as a pointer does not update reference + // count automatically, so this must be done manually + Item._Release; +end; + +class function THashSet.GetAlwaysNaturalItems: Boolean; +begin + Result := true; +end; + +function THashSet.GetIterator: IIterator; +begin + Result := THashIterator.Create(Self); +end; + +function THashSet.GetNaturalItemIID: TGUID; +begin + Result := HashableIID; +end; + +function THashSet.GetCapacity; +begin + Result := FCapacity; +end; + +procedure THashSet.SetCapacity(Value: Integer); +begin + FCapacity := Value; + CheckLoadFactor(true); +end; + +function THashSet.GetSize: Integer; +begin + Result := FSize; +end; + +{ THashIntegerMap } +constructor THashIntegerMap.Create; +begin + Create(false, DefaultTableSize, DefaultMaxLoadFactor); +end; + +constructor THashIntegerMap.Create(NaturalItemsOnly: Boolean); +begin + Create(NaturalItemsOnly, DefaultTableSize, DefaultMaxLoadFactor); +end; + +constructor THashIntegerMap.Create(NaturalItemsOnly: Boolean; TableSize: Integer; MaxLoadFactor: Double = DefaultMaxLoadFactor); +var + I: Integer; +begin + inherited Create(NaturalItemsOnly); + SetLength(FArray, TableSize); + for I := Low(FArray) to High(FArray) do + FArray[I] := TList.Create; + FTableSize := TableSize; + FMaxLoadFactor := MaxLoadFactor; + FSize := 0; +end; + +destructor THashIntegerMap.Destroy; +var + I: Integer; +begin + for I := Low(FArray) to High(FArray) do + FArray[I].Free; + FArray := nil; + inherited Destroy; +end; + +function THashIntegerMap.GetAssociationIterator: IIntegerMapIterator; +begin + Result := THashIntegerIterator.Create(Self); +end; + +procedure THashIntegerMap.SetTableSize(Value: Integer); +begin + if (FTableSize <> Value) and (Value >= 1) then + begin + FTableSize := Value; + Rehash; + end; +end; + +procedure THashIntegerMap.SetMaxLoadFactor(Value: Double); +begin + if (FMaxLoadFactor <> Value) and (Value >= MaxLoadFactorMin) then + begin + FMaxLoadFactor := Value; + CheckLoadFactor(false); + end; +end; + +procedure THashIntegerMap.ChangeCapacity; +var + Chain: TList; + I, Total, ChainCapacity: Integer; +begin + if FCapacity mod FTableSize = 0 then + ChainCapacity := Trunc(FCapacity / FTableSize) + else + ChainCapacity := Trunc(FCapacity / FTableSize) + 1; + Total := 0; + for I := Low(Value) to High(Value) do + begin + Chain := Value[I]; + Chain.Capacity := ChainCapacity; + Total := Total + Chain.Capacity; + end; + FCapacity := Total; +end; + +procedure THashIntegerMap.CheckLoadFactor(AlwaysChangeCapacity: Boolean); +var + LoadFactor: Double; +begin + LoadFactor := Capacity / TableSize; + if LoadFactor > MaxLoadFactor then + TableSize := Trunc(Capacity / Max(MaxLoadFactor, MaxLoadFactorMin)) + else if AlwaysChangeCapacity then + ChangeCapacity(FArray); +end; + +function THashIntegerMap.GetHash(const Key: Integer): Integer; +begin + Result := Trunc(Frac(Cardinal(Key) * HashFactor) * TableSize); +end; + +function THashIntegerMap.GetKeyPosition(const Key: Integer): TCollectionPosition; +var + Chain: TList; + I: Integer; + Success: Boolean; +begin + Chain := FArray[GetHash(Key)]; + Success := false; + for I := 0 to Chain.Count - 1 do + begin + Success := (Key = IIntegerAssociation(Chain[I]).GetKey); + if Success then + Break; + end; + Result := THashPosition.Create(Success, Chain, I); +end; + +procedure THashIntegerMap.Rehash; +var + NewArray: TListArray; + OldChain, NewChain: TList; + Association: IIntegerAssociation; + Total: Integer; + I, J: Integer; + Hash: Integer; +begin + // Create new chains + SetLength(NewArray, TableSize); + for I := Low(NewArray) to High(NewArray) do + begin + NewChain := TList.Create; + NewArray[I] := NewChain; + end; + ChangeCapacity(NewArray); + + // Transfer from old chains to new and drop old + for I := Low(FArray) to High(FArray) do + begin + OldChain := FArray[I]; + for J := 0 to OldChain.Count - 1 do + begin + Association := IIntegerAssociation(OldChain[J]); + Hash := GetHash(Association.GetKey); + NewArray[Hash].Add(Pointer(Association)); + end; + OldChain.Free; + end; + FArray := NewArray; + + // Find actual, new capacity + Total := 0; + for I := Low(FArray) to High(FArray) do + begin + NewChain := FArray[I]; + Total := Total + NewChain.Capacity; + end; + FCapacity := Total; +end; + +procedure THashIntegerMap.TrueClear; +var + Association: IIntegerAssociation; + Chain: TList; + I, J: Integer; +begin + for I := Low(FArray) to High(FArray) do + begin + Chain := FArray[I]; + for J := 0 to Chain.Count - 1 do + begin + Association := IIntegerAssociation(Chain[J]); + Chain[J] := nil; + // Storing interface reference as a pointer does not update reference + // count automatically, so this must be done manually + Association._Release; + end; + Chain.Clear; + end; + FSize := 0; +end; + +function THashIntegerMap.TrueGet(Position: TCollectionPosition): IIntegerAssociation; +var + HashPosition: THashPosition; +begin + HashPosition := THashPosition(Position); + Result := IIntegerAssociation(HashPosition.Chain.Items[HashPosition.Index]); +end; + +function THashIntegerMap.TruePut(Position: TCollectionPosition; const Association: IIntegerAssociation): IIntegerAssociation; +var + HashPosition: THashPosition; + OldAssociation: IIntegerAssociation; +begin + HashPosition := THashPosition(Position); + if HashPosition.Found then + begin + OldAssociation := IIntegerAssociation(HashPosition.Chain.Items[HashPosition.Index]); + HashPosition.Chain.Items[HashPosition.Index] := Pointer(Association); + Result := OldAssociation; + // Storing interface reference as a pointer does not update reference + // count automatically, so this must be done manually + Association._AddRef; + OldAssociation._Release; + end + else + begin + HashPosition.Chain.Add(Pointer(Association)); + Inc(FSize); + Result := nil; + // Storing interface reference as a pointer does not update reference + // count automatically, so this must be done manually + Association._AddRef; + end; +end; + +function THashIntegerMap.TrueRemove2(Position: TCollectionPosition): IIntegerAssociation; +var + Association: IIntegerAssociation; + HashPosition: THashPosition; +begin + HashPosition := THashPosition(Position); + Association := IIntegerAssociation(TrueGet(Position)); + HashPosition.Chain.Delete(HashPosition.Index); + Dec(FSize); + Result := Association; + // Storing interface reference as a pointer does not update reference + // count automatically, so this must be done manually + Association._Release; +end; + +function THashIntegerMap.GetCapacity; +begin + Result := FCapacity; +end; + +procedure THashIntegerMap.SetCapacity(Value: Integer); +begin + FCapacity := Value; + CheckLoadFactor(true); +end; + +function THashIntegerMap.GetSize: Integer; +begin + Result := FSize; +end; + +{ THashStringMap } +constructor THashStringMap.Create; +begin + Create(false, DefaultTableSize, DefaultMaxLoadFactor); +end; + +constructor THashStringMap.Create(NaturalItemsOnly: Boolean); +begin + Create(NaturalItemsOnly, DefaultTableSize, DefaultMaxLoadFactor); +end; + +constructor THashStringMap.Create(NaturalItemsOnly: Boolean; TableSize: Integer; MaxLoadFactor: Double = DefaultMaxLoadFactor); +var + I: Integer; +begin + inherited Create(NaturalItemsOnly); + SetLength(FArray, TableSize); + for I := Low(FArray) to High(FArray) do + FArray[I] := TList.Create; + FTableSize := TableSize; + FMaxLoadFactor := MaxLoadFactor; + FSize := 0; +end; + +destructor THashStringMap.Destroy; +var + I: Integer; +begin + for I := Low(FArray) to High(FArray) do + FArray[I].Free; + FArray := nil; + inherited Destroy; +end; + +function THashStringMap.GetAssociationIterator: IStringMapIterator; +begin + Result := THashStringIterator.Create(Self); +end; + +procedure THashStringMap.SetTableSize(Value: Integer); +begin + if (FTableSize <> Value) and (Value >= 1) then + begin + FTableSize := Value; + Rehash; + end; +end; + +procedure THashStringMap.SetMaxLoadFactor(Value: Double); +begin + if (FMaxLoadFactor <> Value) and (Value >= MaxLoadFactorMin) then + begin + FMaxLoadFactor := Value; + CheckLoadFactor(false); + end; +end; + +procedure THashStringMap.ChangeCapacity; +var + Chain: TList; + I, Total, ChainCapacity: Integer; +begin + if FCapacity mod FTableSize = 0 then + ChainCapacity := Trunc(FCapacity / FTableSize) + else + ChainCapacity := Trunc(FCapacity / FTableSize) + 1; + Total := 0; + for I := Low(Value) to High(Value) do + begin + Chain := Value[I]; + Chain.Capacity := ChainCapacity; + Total := Total + Chain.Capacity; + end; + FCapacity := Total; +end; + +procedure THashStringMap.CheckLoadFactor(AlwaysChangeCapacity: Boolean); +var + LoadFactor: Double; +begin + LoadFactor := Capacity / TableSize; + if LoadFactor > MaxLoadFactor then + TableSize := Trunc(Capacity / Max(MaxLoadFactor, MaxLoadFactorMin)) + else if AlwaysChangeCapacity then + ChangeCapacity(FArray); +end; + +function THashStringMap.GetHash(const Key: String): Integer; +var + HashCode: Cardinal; + I: Integer; +begin + HashCode := 0; + for I := 1 to Length(Key) do + HashCode := (HashCode shl 1) xor Ord(Key[I]); + Result := Trunc(Frac(HashCode * HashFactor) * TableSize); +end; + +function THashStringMap.GetKeyPosition(const Key: String): TCollectionPosition; +var + Chain: TList; + I: Integer; + Success: Boolean; +begin + Chain := FArray[GetHash(Key)]; + Success := false; + for I := 0 to Chain.Count - 1 do + begin + Success := (Key = IStringAssociation(Chain[I]).GetKey); + if Success then + Break; + end; + Result := THashPosition.Create(Success, Chain, I); +end; + +procedure THashStringMap.Rehash; +var + NewArray: TListArray; + OldChain, NewChain: TList; + Association: IStringAssociation; + Total: Integer; + I, J: Integer; + Hash: Integer; +begin + // Create new chains + SetLength(NewArray, TableSize); + for I := Low(NewArray) to High(NewArray) do + begin + NewChain := TList.Create; + NewArray[I] := NewChain; + end; + ChangeCapacity(NewArray); + + // Transfer from old chains to new and drop old + for I := Low(FArray) to High(FArray) do + begin + OldChain := FArray[I]; + for J := 0 to OldChain.Count - 1 do + begin + Association := IStringAssociation(OldChain[J]); + Hash := GetHash(Association.GetKey); + NewArray[Hash].Add(Pointer(Association)); + end; + OldChain.Free; + end; + FArray := NewArray; + + // Find actual, new capacity + Total := 0; + for I := Low(FArray) to High(FArray) do + begin + NewChain := FArray[I]; + Total := Total + NewChain.Capacity; + end; + FCapacity := Total; +end; + +procedure THashStringMap.TrueClear; +var + Association: IStringAssociation; + Chain: TList; + I, J: Integer; +begin + for I := Low(FArray) to High(FArray) do + begin + Chain := FArray[I]; + for J := 0 to Chain.Count - 1 do + begin + Association := IStringAssociation(Chain[J]); + Chain[J] := nil; + // Storing interface reference as a pointer does not update reference + // count automatically, so this must be done manually + Association._Release; + end; + Chain.Clear; + end; + FSize := 0; +end; + +function THashStringMap.TrueGet(Position: TCollectionPosition): IStringAssociation; +var + HashPosition: THashPosition; +begin + HashPosition := THashPosition(Position); + Result := IStringAssociation(HashPosition.Chain.Items[HashPosition.Index]); +end; + +function THashStringMap.TruePut(Position: TCollectionPosition; const Association: IStringAssociation): IStringAssociation; +var + HashPosition: THashPosition; + OldAssociation: IStringAssociation; +begin + HashPosition := THashPosition(Position); + if HashPosition.Found then + begin + OldAssociation := IStringAssociation(HashPosition.Chain.Items[HashPosition.Index]); + HashPosition.Chain.Items[HashPosition.Index] := Pointer(Association); + Result := OldAssociation; + // Storing interface reference as a pointer does not update reference + // count automatically, so this must be done manually + Association._AddRef; + OldAssociation._Release; + end + else + begin + HashPosition.Chain.Add(Pointer(Association)); + Inc(FSize); + Result := nil; + // Storing interface reference as a pointer does not update reference + // count automatically, so this must be done manually + Association._AddRef; + end; +end; + +function THashStringMap.TrueRemove2(Position: TCollectionPosition): IStringAssociation; +var + Association: IStringAssociation; + HashPosition: THashPosition; +begin + HashPosition := THashPosition(Position); + Association := IStringAssociation(TrueGet(Position)); + HashPosition.Chain.Delete(HashPosition.Index); + Dec(FSize); + Result := Association; + // Storing interface reference as a pointer does not update reference + // count automatically, so this must be done manually + Association._Release; +end; + +function THashStringMap.GetCapacity; +begin + Result := FCapacity; +end; + +procedure THashStringMap.SetCapacity(Value: Integer); +begin + FCapacity := Value; + CheckLoadFactor(true); +end; + +function THashStringMap.GetSize: Integer; +begin + Result := FSize; +end; + +{ THashPosition } +constructor THashPosition.Create(Found: Boolean; Chain: TList; Index: Integer); +begin + inherited Create(Found); + FChain := Chain; + FIndex := Index; +end; + +{ THashIterator } +constructor THashIterator.Create(HashSet: THashSet); +begin + inherited Create(true); + FHashSet := HashSet; + First; +end; + +function THashIterator.TrueFirst: ICollectable; +var + Chain: TList; + Success: Boolean; +begin + FHash := 0; + FChainIndex := 0; + Success := false; + while FHash < FHashSet.TableSize do + begin + Chain := FHashSet.FArray[FHash]; + Success := Chain.Count > 0; + if Success then + Break; + Inc(FHash); + end; + if Success then + Result := ICollectable(FHashSet.FArray[FHash].Items[FChainIndex]) + else + Result := nil; +end; + +function THashIterator.TrueNext: ICollectable; +var + Chain: TList; + Success: Boolean; +begin + Success := false; + Chain := FHashSet.FArray[FHash]; + repeat + Inc(FChainIndex); + if FChainIndex >= Chain.Count then + begin + Inc(FHash); + FChainIndex := -1; + if FHash < FHashSet.TableSize then + Chain := FHashSet.FArray[FHash]; + end + else + Success := true; + until Success or (FHash >= FHashSet.TableSize); + if Success then + Result := ICollectable(FHashSet.FArray[FHash].Items[FChainIndex]) + else + Result := nil; +end; + +procedure THashIterator.TrueRemove; +var + Item: ICollectable; +begin + Item := ICollectable(FHashSet.FArray[FHash].Items[FChainIndex]); + FHashSet.FArray[FHash].Delete(FChainIndex); + Dec(FChainIndex); + Dec(FHashSet.FSize); + // Storing interface reference as a pointer does not update reference + // count automatically, so this must be done manually + Item._Release; +end; + + +{ THashAssociationIterator } +constructor THashAssociationIterator.Create(HashMap: THashMap); +begin + inherited Create(true); + FHashMap := HashMap; + First; +end; + +function THashAssociationIterator.TrueFirst: IAssociation; +var + Chain: TList; + Success: Boolean; +begin + FHash := 0; + FChainIndex := 0; + Success := false; + while FHash < FHashMap.TableSize do + begin + Chain := FHashMap.FArray[FHash]; + Success := Chain.Count > 0; + if Success then + Break; + Inc(FHash); + end; + if Success then + Result := IAssociation(FHashMap.FArray[FHash].Items[FChainIndex]) + else + Result := nil; +end; + +function THashAssociationIterator.TrueNext: IAssociation; +var + Chain: TList; + Success: Boolean; +begin + Success := false; + Chain := FHashMap.FArray[FHash]; + repeat + Inc(FChainIndex); + if FChainIndex >= Chain.Count then + begin + Inc(FHash); + FChainIndex := -1; + if FHash < FHashMap.TableSize then + Chain := FHashMap.FArray[FHash]; + end + else + Success := true; + until Success or (FHash >= FHashMap.TableSize); + if Success then + Result := IAssociation(FHashMap.FArray[FHash].Items[FChainIndex]) + else + Result := nil; +end; + +procedure THashAssociationIterator.TrueRemove; +var + Association: IAssociation; +begin + Association := IAssociation(FHashMap.FArray[FHash].Items[FChainIndex]); + FHashMap.FArray[FHash].Delete(FChainIndex); + Dec(FChainIndex); + Dec(FHashMap.FSize); + // Storing interface reference as a pointer does not update reference + // count automatically, so this must be done manually + Association._Release; +end; + + +{ THashIntegerIterator } +constructor THashIntegerIterator.Create(HashIntegerMap: THashIntegerMap); +begin + inherited Create(true); + FHashIntegerMap := HashIntegerMap; + First; +end; + +function THashIntegerIterator.TrueFirst: IIntegerAssociation; +var + Chain: TList; + Success: Boolean; +begin + FHash := 0; + FChainIndex := 0; + Success := false; + while FHash < FHashIntegerMap.TableSize do + begin + Chain := FHashIntegerMap.FArray[FHash]; + Success := Chain.Count > 0; + if Success then + Break; + Inc(FHash); + end; + if Success then + Result := IIntegerAssociation(FHashIntegerMap.FArray[FHash].Items[FChainIndex]) + else + Result := nil; +end; + +function THashIntegerIterator.TrueNext: IIntegerAssociation; +var + Chain: TList; + Success: Boolean; +begin + Success := false; + Chain := FHashIntegerMap.FArray[FHash]; + repeat + Inc(FChainIndex); + if FChainIndex >= Chain.Count then + begin + Inc(FHash); + FChainIndex := -1; + if FHash < FHashIntegerMap.TableSize then + Chain := FHashIntegerMap.FArray[FHash]; + end + else + Success := true; + until Success or (FHash >= FHashIntegerMap.TableSize); + if Success then + Result := IIntegerAssociation(FHashIntegerMap.FArray[FHash].Items[FChainIndex]) + else + Result := nil; +end; + +procedure THashIntegerIterator.TrueRemove; +var + Association: IIntegerAssociation; +begin + Association := IIntegerAssociation(FHashIntegerMap.FArray[FHash].Items[FChainIndex]); + FHashIntegerMap.FArray[FHash].Delete(FChainIndex); + Dec(FChainIndex); + Dec(FHashIntegerMap.FSize); + // Storing interface reference as a pointer does not update reference + // count automatically, so this must be done manually + Association._Release; +end; + +{ THashStringIterator } +constructor THashStringIterator.Create(HashStringMap: THashStringMap); +begin + inherited Create(true); + FHashStringMap := HashStringMap; + First; +end; + +function THashStringIterator.TrueFirst: IStringAssociation; +var + Chain: TList; + Success: Boolean; +begin + FHash := 0; + FChainIndex := 0; + Success := false; + while FHash < FHashStringMap.TableSize do + begin + Chain := FHashStringMap.FArray[FHash]; + Success := Chain.Count > 0; + if Success then + Break; + Inc(FHash); + end; + if Success then + Result := IStringAssociation(FHashStringMap.FArray[FHash].Items[FChainIndex]) + else + Result := nil; +end; + +function THashStringIterator.TrueNext: IStringAssociation; +var + Chain: TList; + Success: Boolean; +begin + Success := false; + Chain := FHashStringMap.FArray[FHash]; + repeat + Inc(FChainIndex); + if FChainIndex >= Chain.Count then + begin + Inc(FHash); + FChainIndex := -1; + if FHash < FHashStringMap.TableSize then + Chain := FHashStringMap.FArray[FHash]; + end + else + Success := true; + until Success or (FHash >= FHashStringMap.TableSize); + if Success then + Result := IStringAssociation(FHashStringMap.FArray[FHash].Items[FChainIndex]) + else + Result := nil; +end; + +procedure THashStringIterator.TrueRemove; +var + Association: IStringAssociation; +begin + Association := IStringAssociation(FHashStringMap.FArray[FHash].Items[FChainIndex]); + FHashStringMap.FArray[FHash].Delete(FChainIndex); + Dec(FChainIndex); + Dec(FHashStringMap.FSize); + // Storing interface reference as a pointer does not update reference + // count automatically, so this must be done manually + Association._Release; +end; + + +end. diff --git a/Lua/src/lib/collections/CollLibrary.pas b/Lua/src/lib/collections/CollLibrary.pas new file mode 100644 index 00000000..b7e3d268 --- /dev/null +++ b/Lua/src/lib/collections/CollLibrary.pas @@ -0,0 +1,131 @@ +unit CollLibrary; + +(***************************************************************************** + * Copyright 2003 by Matthew Greet + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * This library 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 Lesser General Public License for more + * details. (http://opensource.org/licenses/lgpl-license.php) + * + * See http://www.warmachine.u-net.com/delphi_collections for updates and downloads. + * + * $Version: v1.0.3 $ + * $Revision: 1.0.1.1 $ + * $Log: D:\QVCS Repositories\Delphi Collections\CollLibrary.qbt $ + * + * Initial version. + * + * Revision 1.0.1.1 by: Matthew Greet Rev date: 24/10/03 16:48:16 + * v1.0 branch. + * + * Revision 1.0 by: Matthew Greet Rev date: 06/04/03 10:40:32 + * Initial revision. + * + * FPC compatibility fixes by: UltraStar Deluxe Team + * + * $Endlog$ + *****************************************************************************) + +{$IFDEF FPC} + {$MODE Delphi}{$H+} +{$ENDIF} + +interface + +uses + Collections, CollArray, CollHash, CollList, CollPArray, CollWrappers; + +type + TMiscCollectionLibrary = class + public + class function ClassNameToClassType(ClassName: String): TAbstractCollectionClass; + class function EqualIID(const IID1, IID2: TGUID): Boolean; + class function HashCode(Value: String): Integer; + class procedure ShuffleArray(var ItemArray: array of ICollectable); + class procedure ShuffleList(const List: IList); + end; + +implementation + +{ TMiscCollectionLibrary } +class function TMiscCollectionLibrary.ClassNameToClassType(ClassName: String): TAbstractCollectionClass; +begin + if ClassName = 'TArray' then + Result := TArray + else if ClassName = 'THashSet' then + Result := THashSet + else if ClassName = 'THashMap' then + Result := THashMap + else if ClassName = 'THashIntegerMap' then + Result := THashIntegerMap + else if ClassName = 'THashStringMap' then + Result := THashStringMap + else if ClassName = 'TListSet' then + Result := TListSet + else if ClassName = 'TListMap' then + Result := TListMap + else if ClassName = 'TPArrayBag' then + Result := TPArrayBag + else if ClassName = 'TPArraySet' then + Result := TPArraySet + else if ClassName = 'TPArrayList' then + Result := TPArrayList + else if ClassName = 'TPArrayMap' then + Result := TPArrayMap + else + Result := nil; +end; + +class function TMiscCollectionLibrary.EqualIID(const IID1, IID2: TGUID): Boolean; +begin + Result := (IID1.D1 = IID2.D1) and (IID1.D2 = IID2.D2) and (IID1.D3 = IID2.D3) and + (IID1.D4[0] = IID2.D4[0]) and (IID1.D4[1] = IID2.D4[1]) and + (IID1.D4[2] = IID2.D4[2]) and (IID1.D4[3] = IID2.D4[3]) and + (IID1.D4[4] = IID2.D4[4]) and (IID1.D4[5] = IID2.D4[5]) and + (IID1.D4[6] = IID2.D4[6]) and (IID1.D4[7] = IID2.D4[7]); +end; + +class function TMiscCollectionLibrary.HashCode(Value: String): Integer; +var + I: Integer; +begin + Result := 0; + for I := 1 to Length(Value) do + Result := (Result shl 1) xor Ord(Value[I]); +end; + +class procedure TMiscCollectionLibrary.ShuffleArray(var ItemArray: array of ICollectable); +var + Item: ICollectable; + ArraySize, I, Index: Integer; +begin + Randomize; + ArraySize := Length(ItemArray); + for I := 0 to ArraySize - 1 do + begin + Index := (I + Random(ArraySize - 1) + 1) mod ArraySize; + Item := ItemArray[I]; + ItemArray[I] := ItemArray[Index]; + ItemArray[Index] := Item; + end; +end; + +class procedure TMiscCollectionLibrary.ShuffleList(const List: IList); +var + ListSize, I: Integer; +begin + Randomize; + ListSize := List.GetSize; + for I := 0 to ListSize - 1 do + begin + List.Exchange(I, (I + Random(ListSize - 1) + 1) mod ListSize); + end; +end; + + +end. diff --git a/Lua/src/lib/collections/CollList.pas b/Lua/src/lib/collections/CollList.pas new file mode 100644 index 00000000..68aa0d66 --- /dev/null +++ b/Lua/src/lib/collections/CollList.pas @@ -0,0 +1,270 @@ +unit CollList; + +(***************************************************************************** + * Copyright 2003 by Matthew Greet + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * This library 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 Lesser General Public License for more + * details. (http://opensource.org/licenses/lgpl-license.php) + * + * See http://www.warmachine.u-net.com/delphi_collections for updates and downloads. + * + * $Version: v1.0.3 $ + * $Revision: 1.1.1.2 $ + * $Log: D:\QVCS Repositories\Delphi Collections\CollList.qbt $ + * + * Collection implementations based on sorted TPArrayList instances. + * + * Revision 1.1.1.2 by: Matthew Greet Rev date: 12/06/04 20:05:54 + * Capacity property. + * + * Revision 1.1.1.1 by: Matthew Greet Rev date: 14/02/04 17:45:38 + * v1.0 branch. + * + * Revision 1.1 by: Matthew Greet Rev date: 06/04/03 10:41:52 + * Uses TExposedPArrayList to improve performance. + * + * Revision 1.0 by: Matthew Greet Rev date: 01/03/03 10:50:02 + * Initial revision. + * + * FPC compatibility fixes by: UltraStar Deluxe Team + * + * $Endlog$ + *****************************************************************************) + +interface + +{$IFDEF FPC} + {$MODE Delphi}{$H+} +{$ENDIF} + +uses + Collections, CollPArray; + +type + TListSet = class(TAbstractSet) + private + FList: TExposedPArrayList; + protected + function GetPosition(const Item: ICollectable): TCollectionPosition; override; + procedure TrueAdd2(Position: TCollectionPosition; const Item: ICollectable); override; + procedure TrueClear; override; + function TrueGet(Position: TCollectionPosition): ICollectable; override; + procedure TrueRemove2(Position: TCollectionPosition); override; + public + constructor Create(NaturalItemsOnly: Boolean); override; + destructor Destroy; override; + function GetCapacity: Integer; override; + procedure SetCapacity(Value: Integer); override; + function GetIterator: IIterator; override; + function GetNaturalItemIID: TGUID; override; + function GetSize: Integer; override; + end; + + TListMap = class(TAbstractMap) + private + FList: TExposedPArrayList; + protected + function GetAssociationIterator: IMapIterator; override; + function GetKeyPosition(const Key: ICollectable): TCollectionPosition; override; + procedure TrueClear; override; + function TrueGet(Position: TCollectionPosition): IAssociation; override; + function TruePut(Position: TCollectionPosition; const Association: IAssociation): IAssociation; override; + function TrueRemove2(Position: TCollectionPosition): IAssociation; override; + public + constructor Create(NaturalItemsOnly: Boolean; NaturalKeysOnly: Boolean); override; + destructor Destroy; override; + function GetCapacity: Integer; override; + procedure SetCapacity(Value: Integer); override; + procedure SetKeyComparator(const Value: IComparator); override; + function GetNaturalKeyIID: TGUID; override; + function GetSize: Integer; override; + end; + +implementation + +type + TListPosition = class(TCollectionPosition) + private + FSearchResult: TSearchResult; + public + constructor Create(Found: Boolean; SearchResult: TSearchResult); + property SearchResult: TSearchResult read FSearchResult; + end; + +constructor TListSet.Create(NaturalItemsOnly: Boolean); +begin + inherited Create(NaturalItemsOnly); + FList := TExposedPArrayList.Create(NaturalItemsOnly); + FList.Comparator := Comparator; + FList.Sort; +end; + +destructor TListSet.Destroy; +begin + FList.Free; + inherited Destroy; +end; + +function TListSet.GetPosition(const Item: ICollectable): TCollectionPosition; +var + SearchResult: TSearchResult; +begin + SearchResult := FList.Search(Item); + Result := TListPosition.Create((SearchResult.ResultType = srFoundAtIndex), SearchResult); +end; + +procedure TListSet.TrueAdd2(Position: TCollectionPosition; const Item: ICollectable); +var + SearchResult: TSearchResult; + Index: Integer; +begin + SearchResult := TListPosition(Position).SearchResult; + Index := SearchResult.Index; + if SearchResult.ResultType = srBeforeIndex then + FList.TrueInsert(Index, Item) + else + FList.TrueAppend(Item); +end; + +procedure TListSet.TrueClear; +begin + FList.Clear; +end; + +function TListSet.TrueGet(Position: TCollectionPosition): ICollectable; +begin + Result := FList.Items[TListPosition(Position).SearchResult.Index]; +end; + +procedure TListSet.TrueRemove2(Position: TCollectionPosition); +begin + FList.Delete(TListPosition(Position).SearchResult.Index); +end; + +function TListSet.GetCapacity: Integer; +begin + Result := FList.Capacity; +end; + +procedure TListSet.SetCapacity(Value: Integer); +begin + FList.Capacity := Value; +end; + +function TListSet.GetIterator: IIterator; +begin + Result := FList.GetIterator; +end; + +function TListSet.GetNaturalItemIID: TGUID; +begin + Result := ComparableIID; +end; + +function TListSet.GetSize: Integer; +begin + Result := FList.Size; +end; + +constructor TListMap.Create(NaturalItemsOnly: Boolean; NaturalKeysOnly: Boolean); +begin + inherited Create(NaturalItemsOnly, NaturalKeysOnly); + FList := TExposedPArrayList.Create(false); + FList.Comparator := AssociationComparator; + FList.Sort; +end; + +destructor TListMap.Destroy; +begin + FList.Free; + inherited Destroy; +end; + +function TListMap.GetAssociationIterator: IMapIterator; +begin + Result := TAssociationIterator.Create(FList.GetIterator); +end; + +function TListMap.GetKeyPosition(const Key: ICollectable): TCollectionPosition; +var + Association: IAssociation; + SearchResult: TSearchResult; +begin + Association := TAssociation.Create(Key, nil); + SearchResult := FList.Search(Association); + Result := TListPosition.Create((SearchResult.ResultType = srFoundAtIndex), SearchResult); +end; + +procedure TListMap.TrueClear; +begin + FList.Clear; +end; + +function TListMap.TrueGet(Position: TCollectionPosition): IAssociation; +begin + Result := (FList.Items[TListPosition(Position).SearchResult.Index]) as IAssociation; +end; + +function TListMap.TruePut(Position: TCollectionPosition; const Association: IAssociation): IAssociation; +var + SearchResult: TSearchResult; + Index: Integer; +begin + SearchResult := TListPosition(Position).SearchResult; + Index := SearchResult.Index; + if SearchResult.ResultType = srFoundAtIndex then + begin + Result := (FList.Items[Index]) as IAssociation; + FList.Items[Index] := Association; + end + else if SearchResult.ResultType = srBeforeIndex then + FList.TrueInsert(Index, Association) + else + FList.TrueAppend(Association); +end; + +function TListMap.TrueRemove2(Position: TCollectionPosition): IAssociation; +begin + Result := (FList.Items[TListPosition(Position).SearchResult.Index]) as IAssociation; + FList.Delete(TListPosition(Position).SearchResult.Index); +end; + +procedure TListMap.SetKeyComparator(const Value: IComparator); +begin + inherited SetKeyComparator(Value); + FList.Sort; +end; + +function TListMap.GetCapacity: Integer; +begin + Result := FList.Capacity; +end; + +procedure TListMap.SetCapacity(Value: Integer); +begin + FList.Capacity := Value; +end; + +function TListMap.GetNaturalKeyIID: TGUID; +begin + Result := ComparableIID; +end; + +function TListMap.GetSize: Integer; +begin + Result := FList.Size; +end; + +constructor TListPosition.Create(Found: Boolean; SearchResult: TSearchResult); +begin + inherited Create(Found); + FSearchResult := SearchResult; +end; + +end. diff --git a/Lua/src/lib/collections/CollPArray.pas b/Lua/src/lib/collections/CollPArray.pas new file mode 100644 index 00000000..5ebd534b --- /dev/null +++ b/Lua/src/lib/collections/CollPArray.pas @@ -0,0 +1,689 @@ +unit CollPArray; + +(***************************************************************************** + * Copyright 2003 by Matthew Greet + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * This library 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 Lesser General Public License for more + * details. (http://opensource.org/licenses/lgpl-license.php) + * + * See http://www.warmachine.u-net.com/delphi_collections for updates and downloads. + * + * $Version: v1.0.3 $ + * $Revision: 1.2.1.2 $ + * $Log: D:\QVCS Repositories\Delphi Collections\CollPArray.qbt $ + * + * Collection implementations based on TList. + * + * Revision 1.2.1.2 by: Matthew Greet Rev date: 12/06/04 20:08:30 + * Capacity property. + * + * Revision 1.2.1.1 by: Matthew Greet Rev date: 14/02/04 17:46:10 + * v1.0 branch. + * + * Revision 1.2 by: Matthew Greet Rev date: 28/04/03 15:07:14 + * Correctly handles nil items. + * + * Revision 1.1 by: Matthew Greet Rev date: 06/04/03 10:43:16 + * Added TPArrayMap and TExposedPArrayList. + * + * Revision 1.0 by: Matthew Greet Rev date: 01/03/03 10:50:02 + * Initial revision. + * + * FPC compatibility fixes by: UltraStar Deluxe Team + * + * $Endlog$ + *****************************************************************************) + +{$IFDEF FPC} + {$MODE Delphi}{$H+} +{$ENDIF} + +interface + +uses + Classes, + Collections; + +type + TPArrayBag = class(TAbstractBag) + private + FList: TList; + protected + function TrueAdd(const Item: ICollectable): Boolean; override; + procedure TrueClear; override; + function TrueRemove(const Item: ICollectable): ICollectable; override; + function TrueRemoveAll(const Item: ICollectable): ICollection; override; + public + constructor Create(NaturalItemsOnly: Boolean); override; + destructor Destroy; override; + function GetCapacity: Integer; override; + procedure SetCapacity(Value: Integer); override; + function GetIterator: IIterator; override; + function GetSize: Integer; override; + function TrueContains(const Item: ICollectable): Boolean; override; + end; + + TPArraySet = class(TAbstractSet) + private + FList: TList; + protected + function GetPosition(const Item: ICollectable): TCollectionPosition; override; + procedure TrueAdd2(Position: TCollectionPosition; const Item: ICollectable); override; + procedure TrueClear; override; + function TrueGet(Position: TCollectionPosition): ICollectable; override; + procedure TrueRemove2(Position: TCollectionPosition); override; + public + constructor Create(NaturalItemsOnly: Boolean); override; + destructor Destroy; override; + function GetCapacity: Integer; override; + procedure SetCapacity(Value: Integer); override; + function GetIterator: IIterator; override; + function GetSize: Integer; override; + end; + + TPArrayList = class(TAbstractList) + private + FList: TList; + protected + function TrueGetItem(Index: Integer): ICollectable; override; + procedure TrueSetItem(Index: Integer; const Item: ICollectable); override; + procedure TrueAppend(const Item: ICollectable); override; + procedure TrueClear; override; + function TrueDelete(Index: Integer): ICollectable; override; + procedure TrueInsert(Index: Integer; const Item: ICollectable); override; + public + constructor Create(NaturalItemsOnly: Boolean); override; + destructor Destroy; override; + function GetCapacity: Integer; override; + procedure SetCapacity(Value: Integer); override; + function GetIterator: IIterator; override; + function GetSize: Integer; override; + end; + + TPArrayMap = class(TAbstractMap) + private + FList: TList; + protected + function GetAssociationIterator: IMapIterator; override; + function GetKeyPosition(const Key: ICollectable): TCollectionPosition; override; + procedure TrueClear; override; + function TrueGet(Position: TCollectionPosition): IAssociation; override; + function TruePut(Position: TCollectionPosition; const Association: IAssociation): IAssociation; override; + function TrueRemove2(Position: TCollectionPosition): IAssociation; override; + public + constructor Create(NaturalItemsOnly: Boolean; NaturalKeysOnly: Boolean); override; + destructor Destroy; override; + function GetCapacity: Integer; override; + procedure SetCapacity(Value: Integer); override; + function GetSize: Integer; override; + end; + + // Same as TPArrayList but raises method visibilities so items can be manually + // appended or inserted without resetting sort flag. + TExposedPArrayList = class(TPArrayList) + public + procedure TrueAppend(const Item: ICollectable); override; + procedure TrueInsert(Index: Integer; const Item: ICollectable); override; + end; + + +implementation + +type + TPArrayIterator = class(TAbstractIterator) + private + FList: TList; + FIndex: Integer; + protected + constructor Create(List: TList; AllowRemove: Boolean); + function TrueFirst: ICollectable; override; + function TrueNext: ICollectable; override; + procedure TrueRemove; override; + end; + + TPArrayAssociationIterator = class(TAbstractAssociationIterator) + private + FList: TList; + FIndex: Integer; + protected + constructor Create(List: TList; AllowRemove: Boolean); + function TrueFirst: IAssociation; override; + function TrueNext: IAssociation; override; + procedure TrueRemove; override; + end; + + TPArrayPosition = class(TCollectionPosition) + private + FIndex: Integer; + public + constructor Create(Found: Boolean; Index: Integer); + property Index: Integer read FIndex; + end; + +constructor TPArrayBag.Create(NaturalItemsOnly: Boolean); +begin + inherited Create(NaturalItemsOnly); + FList := TList.Create; +end; + +destructor TPArrayBag.Destroy; +begin + FList.Free; + inherited Destroy; +end; + +function TPArrayBag.TrueAdd(const Item: ICollectable): Boolean; +begin + FList.Add(Pointer(Item)); + Result := true; + // Storing interface reference as a pointer does not update reference + // count automatically, so this must be done manually + if Item <> nil then + Item._AddRef; +end; + +procedure TPArrayBag.TrueClear; +var + Item: ICollectable; + I: Integer; +begin + // Delete all interface references + for I := 0 to FList.Count - 1 do + begin + Item := ICollectable(FList[I]); + FList[I] := nil; + // Storing interface reference as a pointer does not update reference + // count automatically, so this must be done manually + if Item <> nil then + Item._Release; + end; + FList.Clear; +end; + +function TPArrayBag.TrueContains(const Item: ICollectable): Boolean; +var + I: Integer; + Success: Boolean; +begin + // Sequential search + I := 0; + Success := false; + while (I < FList.Count) and not Success do + begin + Success := Comparator.Equals(Item, ICollectable(FList[I])); + Inc(I); + end; + Result := Success; +end; + +function TPArrayBag.TrueRemove(const Item: ICollectable): ICollectable; +var + Item2: ICollectable; + I: Integer; + Found: Boolean; +begin + // Sequential search + I := 0; + Found := false; + Result := nil; + while (I < FList.Count) and not Found do + begin + Item2 := ICollectable(FList[I]); + if Comparator.Equals(Item, Item2) then + begin + Found := true; + Result := Item2; + FList.Delete(I); + // Storing interface reference as a pointer does not update reference + // count automatically, so this must be done manually + if Item2 <> nil then + Item2._Release; + end + else + Inc(I); + end; +end; + +function TPArrayBag.TrueRemoveAll(const Item: ICollectable): ICollection; +var + ResultCollection: TPArrayBag; + Item2: ICollectable; + I: Integer; +begin + // Sequential search + I := 0; + ResultCollection := TPArrayBag.Create; + while I < FList.Count do + begin + Item2 := ICollectable(FList[I]); + if Comparator.Equals(Item, Item2) then + begin + ResultCollection.Add(Item2); + FList.Delete(I); + // Storing interface reference as a pointer does not update reference + // count automatically, so this must be done manually + if Item <> nil then + Item._Release; + end + else + Inc(I); + end; + Result := ResultCollection; +end; + +function TPArrayBag.GetCapacity: Integer; +begin + Result := FList.Capacity; +end; + +procedure TPArrayBag.SetCapacity(Value: Integer); +begin + FList.Capacity := Value; +end; + +function TPArrayBag.GetIterator: IIterator; +begin + Result := TPArrayIterator.Create(FList, true); +end; + +function TPArrayBag.GetSize: Integer; +begin + Result := FList.Count; +end; + +constructor TPArraySet.Create(NaturalItemsOnly: Boolean); +begin + inherited Create(NaturalItemsOnly); + FList := TList.Create; +end; + +destructor TPArraySet.Destroy; +begin + FList.Free; + inherited Destroy; +end; + +function TPArraySet.GetPosition(const Item: ICollectable): TCollectionPosition; +var + I: Integer; + Success: Boolean; +begin + // Sequential search + I := 0; + Success := false; + while (I < FList.Count) do + begin + Success := Comparator.Equals(Item, ICollectable(FList[I])); + if Success then + break; + Inc(I); + end; + Result := TPArrayPosition.Create(Success, I); +end; + +procedure TPArraySet.TrueAdd2(Position: TCollectionPosition; const Item: ICollectable); +begin + FList.Add(Pointer(Item)); + // Storing interface reference as a pointer does not update reference + // count automatically, so this must be done manually + Item._AddRef; +end; + +procedure TPArraySet.TrueClear; +var + Item: ICollectable; + I: Integer; +begin + // Delete all interface references + for I := 0 to FList.Count - 1 do + begin + Item := ICollectable(FList[I]); + FList[I] := nil; + // Storing interface reference as a pointer does not update reference + // count automatically, so this must be done manually + Item._Release; + end; + FList.Clear; +end; + +function TPArraySet.TrueGet(Position: TCollectionPosition): ICollectable; +begin + Result := ICollectable(FList.Items[TPArrayPosition(Position).Index]); +end; + +procedure TPArraySet.TrueRemove2(Position: TCollectionPosition); +var + Item: ICollectable; +begin + Item := ICollectable(FList[TPArrayPosition(Position).Index]); + FList.Delete(TPArrayPosition(Position).Index); + // Storing interface reference as a pointer does not update reference + // count automatically, so this must be done manually + Item._Release; +end; + +function TPArraySet.GetCapacity: Integer; +begin + Result := FList.Capacity; +end; + +procedure TPArraySet.SetCapacity(Value: Integer); +begin + FList.Capacity := Value; +end; + +function TPArraySet.GetIterator: IIterator; +begin + Result := TPArrayIterator.Create(FList, true); +end; + +function TPArraySet.GetSize: Integer; +begin + Result := FList.Count; +end; + +constructor TPArrayList.Create(NaturalItemsOnly: Boolean); +begin + inherited Create(NaturalItemsOnly); + FList := TList.Create; +end; + +destructor TPArrayList.Destroy; +begin + FList.Free; + inherited Destroy; +end; + +function TPArrayList.TrueGetItem(Index: Integer): ICollectable; +begin + Result := ICollectable(FList.Items[Index]); +end; + +procedure TPArrayList.TrueSetItem(Index: Integer; const Item: ICollectable); +var + OldItem: ICollectable; +begin + OldItem := ICollectable(FList[Index]); + FList[Index] := Pointer(Item); + // Storing interface reference as a pointer does not update reference + // count automatically, so this must be done manually + if Item <> nil then + Item._AddRef; + if OldItem <> nil then + OldItem._Release; +end; + +procedure TPArrayList.TrueAppend(const Item: ICollectable); +begin + FList.Add(Pointer(Item)); + // Storing interface reference as a pointer does not update reference + // count automatically, so this must be done manually + if Item <> nil then + Item._AddRef; +end; + +procedure TPArrayList.TrueClear; +var + Item: ICollectable; + I: Integer; +begin + // Delete all interface references + for I := 0 to FList.Count - 1 do + begin + Item := ICollectable(FList[I]); + FList[I] := nil; + // Storing interface reference as a pointer does not update reference + // count automatically, so this must be done manually + if Item <> nil then + Item._Release; + end; + FList.Clear; +end; + +function TPArrayList.TrueDelete(Index: Integer): ICollectable; +begin + Result := ICollectable(FList[Index]); + FList.Delete(Index); + // Storing interface reference as a pointer does not update reference + // count automatically, so this must be done manually + if Result <> nil then + Result._Release; +end; + +procedure TPArrayList.TrueInsert(Index: Integer; const Item: ICollectable); +begin + FList.Insert(Index, Pointer(Item)); + // Storing interface reference as a pointer does not update reference + // count automatically, so this must be done manually + if Item <> nil then + Item._AddRef; +end; + +function TPArrayList.GetCapacity: Integer; +begin + Result := FList.Capacity; +end; + +procedure TPArrayList.SetCapacity(Value: Integer); +begin + FList.Capacity := Value; +end; + +function TPArrayList.GetIterator: IIterator; +begin + Result := TPArrayIterator.Create(FList, true); +end; + +function TPArrayList.GetSize: Integer; +begin + Result := FList.Count; +end; + +constructor TPArrayMap.Create(NaturalItemsOnly: Boolean; NaturalKeysOnly: Boolean); +begin + inherited Create(NaturalItemsOnly, NaturalKeysOnly); + FList := TList.Create; +end; + +destructor TPArrayMap.Destroy; +begin + FList.Free; + inherited Destroy; +end; + +function TPArrayMap.GetAssociationIterator: IMapIterator; +begin + Result := TPArrayAssociationIterator.Create(FList, true); +end; + +function TPArrayMap.GetKeyPosition(const Key: ICollectable): TCollectionPosition; +var + I: Integer; + Success: Boolean; +begin + // Sequential search + I := 0; + Success := false; + while (I < FList.Count) do + begin + Success := KeyComparator.Equals(Key, IAssociation(FList[I]).GetKey); + if Success then + break; + Inc(I); + end; + Result := TPArrayPosition.Create(Success, I); +end; + +procedure TPArrayMap.TrueClear; +var + Association: IAssociation; + I: Integer; +begin + // Delete all interface references + for I := 0 to FList.Count - 1 do + begin + Association := IAssociation(FList[I]); + FList[I] := nil; + // Storing interface reference as a pointer does not update reference + // count automatically, so this must be done manually + Association._Release; + end; + FList.Clear; +end; + +function TPArrayMap.TrueGet(Position: TCollectionPosition): IAssociation; +begin + Result := IAssociation(FList.Items[TPArrayPosition(Position).Index]); +end; + +function TPArrayMap.TruePut(Position: TCollectionPosition; const Association: IAssociation): IAssociation; +var + OldAssociation: IAssociation; + Index: Integer; +begin + if Position.Found then + begin + Index := (Position as TPArrayPosition).Index; + OldAssociation := IAssociation(FList[Index]); + FList[Index] := Pointer(Association); + end + else + begin + OldAssociation := nil; + FList.Add(Pointer(Association)); + end; + Result := OldAssociation; + // Storing interface reference as a pointer does not update reference + // count automatically, so this must be done manually + Association._AddRef; + if OldAssociation <> nil then + OldAssociation._Release; +end; + +function TPArrayMap.TrueRemove2(Position: TCollectionPosition): IAssociation; +var + OldAssociation: IAssociation; +begin + OldAssociation := IAssociation(FList[TPArrayPosition(Position).Index]); + FList.Delete(TPArrayPosition(Position).Index); + Result := OldAssociation; + // Storing interface reference as a pointer does not update reference + // count automatically, so this must be done manually + OldAssociation._Release; +end; + +function TPArrayMap.GetCapacity: Integer; +begin + Result := FList.Capacity; +end; + +procedure TPArrayMap.SetCapacity(Value: Integer); +begin + FList.Capacity := Value; +end; + +function TPArrayMap.GetSize: Integer; +begin + Result := FList.Count; +end; + +procedure TExposedPArrayList.TrueAppend(const Item: ICollectable); +begin + inherited TrueAppend(Item); +end; + +procedure TExposedPArrayList.TrueInsert(Index: Integer; const Item: ICollectable); +begin + inherited TrueInsert(Index, Item); +end; + +{ TPArrayIterator } +constructor TPArrayIterator.Create(List: TList; AllowRemove: Boolean); +begin + inherited Create(AllowRemove); + FList := List; + FIndex := -1; +end; + +function TPArrayIterator.TrueFirst: ICollectable; +begin + FIndex := 0; + if FIndex < FList.Count then + Result := ICollectable(FList[FIndex]) + else + Result := nil; +end; + +function TPArrayIterator.TrueNext: ICollectable; +begin + Inc(FIndex); + if FIndex < FList.Count then + Result := ICollectable(FList[FIndex]) + else + Result := nil; +end; + +procedure TPArrayIterator.TrueRemove; +var + Item: ICollectable; +begin + Item := ICollectable(FList[FIndex]); + FList.Delete(FIndex); + Dec(FIndex); + // Storing interface reference as a pointer does not update reference + // count automatically, so this must be done manually + Item._Release; +end; + +{ TPArrayAssociationIterator } +constructor TPArrayAssociationIterator.Create(List: TList; AllowRemove: Boolean); +begin + inherited Create(AllowRemove); + FList := List; + FIndex := -1; +end; + +function TPArrayAssociationIterator.TrueFirst: IAssociation; +begin + FIndex := 0; + if FIndex < FList.Count then + Result := IAssociation(FList[FIndex]) + else + Result := nil; +end; + +function TPArrayAssociationIterator.TrueNext: IAssociation; +begin + Inc(FIndex); + if FIndex < FList.Count then + Result := IAssociation(FList[FIndex]) + else + Result := nil; +end; + +procedure TPArrayAssociationIterator.TrueRemove; +var + Association: IAssociation; +begin + Association := IAssociation(FList[FIndex]); + FList.Delete(FIndex); + Dec(FIndex); + // Storing interface reference as a pointer does not update reference + // count automatically, so this must be done manually + Association._Release; +end; + +{ TPArrayPosition } +constructor TPArrayPosition.Create(Found: Boolean; Index: Integer); +begin + inherited Create(Found); + FIndex := Index; +end; + +end. diff --git a/Lua/src/lib/collections/CollWrappers.pas b/Lua/src/lib/collections/CollWrappers.pas new file mode 100644 index 00000000..513103a2 --- /dev/null +++ b/Lua/src/lib/collections/CollWrappers.pas @@ -0,0 +1,876 @@ +unit CollWrappers; + +(***************************************************************************** + * Copyright 2003 by Matthew Greet + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * This library 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 Lesser General Public License for more + * details. (http://opensource.org/licenses/lgpl-license.php) + * + * See http://www.warmachine.u-net.com/delphi_collections for updates and downloads. + * + * $Version: v1.0.3 $ + * $Revision: 1.1.1.1 $ + * $Log: D:\QVCS Repositories\Delphi Collections\CollWrappers.qbt $ + * + * Various primitive type wrappers, adapters and abstract base classes for + * natural items. + * + * Revision 1.1.1.1 by: Matthew Greet Rev date: 24/10/03 16:48:16 + * v1.0 branch. + * + * Revision 1.1 by: Matthew Greet Rev date: 06/04/03 10:51:04 + * Primitive type wrapper interfaces added. + * Abstract, template classes added. + * All classes implement reference counting by descending from + * TInterfacedObject. + * + * + * Revision 1.0 by: Matthew Greet Rev date: 01/03/03 10:50:02 + * Initial revision. + * + * FPC compatibility fixes by: UltraStar Deluxe Team + * + * $Endlog$ + *****************************************************************************) + +{$IFDEF FPC} + {$MODE Delphi}{$H+} +{$ENDIF} + +interface + +uses + SysUtils, + Collections; + +type + IAssociationWrapper = interface + ['{54DF42E0-64F2-11D7-8120-0002E3165EF8}'] + function GetAutoDestroy: Boolean; + procedure SetAutoDestroy(Value: Boolean); + function GetKey: ICollectable; + function GetValue: TObject; + property AutoDestroy: Boolean read GetAutoDestroy write SetAutoDestroy; + property Key: ICollectable read GetKey; + property Value: TObject read GetValue; + end; + + IBoolean = interface + ['{62D1D160-64F2-11D7-8120-0002E3165EF8}'] + function GetValue: Boolean; + property Value: Boolean read GetValue; + end; + + ICardinal = interface + ['{6AF7B1C0-64F2-11D7-8120-0002E3165EF8}'] + function GetValue: Cardinal; + property Value: Cardinal read GetValue; + end; + + IChar = interface + ['{73AD00E0-64F2-11D7-8120-0002E3165EF8}'] + function GetValue: Char; + property Value: Char read GetValue; + end; + + IClass = interface + ['{7A84B660-64F2-11D7-8120-0002E3165EF8}'] + function GetValue: TClass; + property Value: TClass read GetValue; + end; + + IDouble = interface + ['{815C6BE0-64F2-11D7-8120-0002E3165EF8}'] + function GetValue: Double; + property Value: Double read GetValue; + end; + + IInteger = interface + ['{88ECC300-64F2-11D7-8120-0002E3165EF8}'] + function GetValue: Integer; + property Value: Integer read GetValue; + end; + + IIntegerAssociationWrapper = interface + ['{8F582220-64F2-11D7-8120-0002E3165EF8}'] + function GetAutoDestroy: Boolean; + procedure SetAutoDestroy(Value: Boolean); + function GetKey: Integer; + function GetValue: TObject; + property AutoDestroy: Boolean read GetAutoDestroy write SetAutoDestroy; + property Key: Integer read GetKey; + property Value: TObject read GetValue; + end; + + IInterfaceWrapper = interface + ['{962E5100-64F2-11D7-8120-0002E3165EF8}'] + function GetValue: IUnknown; + property Value: IUnknown read GetValue; + end; + + IObject = interface + ['{9C675580-64F2-11D7-8120-0002E3165EF8}'] + function GetAutoDestroy: Boolean; + procedure SetAutoDestroy(Value: Boolean); + function GetValue: TObject; + property Value: TObject read GetValue; + end; + + IString = interface + ['{A420DF80-64F2-11D7-8120-0002E3165EF8}'] + function GetValue: String; + property Value: String read GetValue; + end; + + IStringAssociationWrapper = interface + ['{AB98CCA0-64F2-11D7-8120-0002E3165EF8}'] + function GetAutoDestroy: Boolean; + procedure SetAutoDestroy(Value: Boolean); + function GetKey: String; + function GetValue: TObject; + property AutoDestroy: Boolean read GetAutoDestroy write SetAutoDestroy; + property Key: String read GetKey; + property Value: TObject read GetValue; + end; + + TAbstractItem = class(TInterfacedObject, ICollectable) + public + function GetInstance: TObject; virtual; + end; + + TAbstractIntegerMappable = class(TAbstractItem, IEquatable, IIntegerMappable) + private + FKey: Integer; + protected + function MakeKey: Integer; virtual; abstract; + public + procedure AfterConstruction; override; + function Equals(const Item: ICollectable): Boolean; virtual; + function GetKey: Integer; virtual; + end; + + TAbstractMappable = class(TAbstractItem, IEquatable, IMappable) + private + FKey: ICollectable; + protected + function MakeKey: ICollectable; virtual; abstract; + public + destructor Destroy; override; + procedure AfterConstruction; override; + function Equals(const Item: ICollectable): Boolean; virtual; + function GetKey: ICollectable; virtual; + end; + + TAbstractStringMappable = class(TAbstractItem, IEquatable, IStringMappable) + private + FKey: String; + protected + function MakeKey: String; virtual; abstract; + public + procedure AfterConstruction; override; + function Equals(const Item: ICollectable): Boolean; virtual; + function GetKey: String; virtual; + end; + + TAssociationWrapper = class(TAbstractItem, IEquatable, IMappable, IAssociationWrapper) + private + FAutoDestroy: Boolean; + FKey: ICollectable; + FValue: TObject; + public + constructor Create(const Key: ICollectable; Value: TObject); overload; + constructor Create(Key: Integer; Value: TObject); overload; + constructor Create(Key: String; Value: TObject); overload; + constructor Create(Key, Value: TObject; AutoDestroyKey: Boolean = true); overload; + destructor Destroy; override; + function GetAutoDestroy: Boolean; + procedure SetAutoDestroy(Value: Boolean); + function GetKey: ICollectable; + function GetValue: TObject; + function Equals(const Item: ICollectable): Boolean; + property AutoDestroy: Boolean read GetAutoDestroy write SetAutoDestroy; + property Key: ICollectable read GetKey; + property Value: TObject read GetValue; + end; + + TBooleanWrapper = class(TAbstractItem, IEquatable, IHashable, IComparable, IBoolean) + private + FValue: Boolean; + public + constructor Create(Value: Boolean); + function GetValue: Boolean; + function CompareTo(const Item: ICollectable): Integer; + function Equals(const Item: ICollectable): Boolean; + function HashCode: Integer; + property Value: Boolean read GetValue; + end; + + TCardinalWrapper = class(TAbstractItem, IEquatable, IHashable, IComparable, ICardinal) + private + FValue: Cardinal; + public + constructor Create(Value: Cardinal); + function GetValue: Cardinal; + function Equals(const Item: ICollectable): Boolean; + function HashCode: Integer; + function CompareTo(const Item: ICollectable): Integer; + property Value: Cardinal read GetValue; + end; + + TCharWrapper = class(TAbstractItem, IEquatable, IHashable, IComparable, IChar) + private + FValue: Char; + public + constructor Create(Value: Char); + function GetValue: Char; + function Equals(const Item: ICollectable): Boolean; + function HashCode: Integer; + function CompareTo(const Item: ICollectable): Integer; + property Value: Char read GetValue; + end; + + TClassWrapper = class(TAbstractItem, IEquatable, IHashable, IClass) + private + FValue: TClass; + public + constructor Create(Value: TClass); + function GetValue: TClass; + function Equals(const Item: ICollectable): Boolean; + function HashCode: Integer; + property Value: TClass read GetValue; + end; + + TDoubleWrapper = class(TAbstractItem, IEquatable, IHashable, IComparable, IDouble) + private + FValue: Double; + public + constructor Create(Value: Double); + function GetValue: Double; + function Equals(const Item: ICollectable): Boolean; + function HashCode: Integer; + function CompareTo(const Item: ICollectable): Integer; + property Value: Double read GetValue; + end; + + TIntegerWrapper = class(TAbstractItem, IEquatable, IHashable, IComparable, IInteger) + private + FValue: Integer; + public + constructor Create(Value: Integer); + function GetValue: Integer; + function Equals(const Item: ICollectable): Boolean; + function HashCode: Integer; + function CompareTo(const Item: ICollectable): Integer; + property Value: Integer read GetValue; + end; + + TIntegerAssociationWrapper = class(TAbstractItem, IEquatable, IIntegerMappable, IIntegerAssociationWrapper) + private + FAutoDestroy: Boolean; + FKey: Integer; + FValue: TObject; + public + constructor Create(const Key: Integer; Value: TObject); overload; + destructor Destroy; override; + function Equals(const Item: ICollectable): Boolean; + function GetAutoDestroy: Boolean; + procedure SetAutoDestroy(Value: Boolean); + function GetKey: Integer; + function GetValue: TObject; + property AutoDestroy: Boolean read GetAutoDestroy write SetAutoDestroy; + property Key: Integer read GetKey; + property Value: TObject read GetValue; + end; + + TInterfaceWrapper = class(TAbstractItem, IHashable, IEquatable, IInterfaceWrapper) + private + FValue: IUnknown; + public + constructor Create(const Value: IUnknown); + destructor Destroy; override; + function GetValue: IUnknown; + function Equals(const Item: ICollectable): Boolean; + function HashCode: Integer; + property Value: IUnknown read GetValue; + end; + + TObjectWrapper = class(TAbstractItem, IEquatable, IComparable, IHashable, IObject) + private + FAutoDestroy: Boolean; + FValue: TObject; + public + constructor Create(Value: TObject); overload; + destructor Destroy; override; + function GetAutoDestroy: Boolean; + procedure SetAutoDestroy(Value: Boolean); + function GetValue: TObject; + function CompareTo(const Item: ICollectable): Integer; + function Equals(const Item: ICollectable): Boolean; + function HashCode: Integer; + property AutoDestroy: Boolean read FAutoDestroy write FAutoDestroy; + property Value: TObject read GetValue; + end; + + TStringWrapper = class(TAbstractItem, IEquatable, IHashable, IComparable, IString) + private + FValue: String; + public + constructor Create(Value: String); + function GetValue: String; + function Equals(const Item: ICollectable): Boolean; + function HashCode: Integer; + function CompareTo(const Item: ICollectable): Integer; + property Value: String read FValue; + end; + + TStringAssociationWrapper = class(TAbstractItem, IEquatable, IStringMappable, IStringAssociationWrapper) + private + FAutoDestroy: Boolean; + FKey: String; + FValue: TObject; + public + constructor Create(const Key: String; Value: TObject); overload; + destructor Destroy; override; + function GetAutoDestroy: Boolean; + procedure SetAutoDestroy(Value: Boolean); + function GetKey: String; + function GetValue: TObject; + function Equals(const Item: ICollectable): Boolean; + property AutoDestroy: Boolean read GetAutoDestroy write SetAutoDestroy; + property Key: String read GetKey; + property Value: TObject read GetValue; + end; + +implementation + +{ TAbstractItem } +function TAbstractItem.GetInstance: TObject; +begin + Result := Self; +end; + + +{ TAbstractIntegerMappable } +procedure TAbstractIntegerMappable.AfterConstruction; +begin + inherited AfterConstruction; + FKey := MakeKey; +end; + +function TAbstractIntegerMappable.Equals(const Item: ICollectable): Boolean; +begin + Result := (Self = Item.GetInstance); +end; + +function TAbstractIntegerMappable.GetKey: Integer; +begin + Result := FKey; +end; + +{ TAbstractMappable } +destructor TAbstractMappable.Destroy; +begin + FKey := nil; + inherited Destroy; +end; + +procedure TAbstractMappable.AfterConstruction; +begin + inherited AfterConstruction; + FKey := MakeKey; +end; + +function TAbstractMappable.Equals(const Item: ICollectable): Boolean; +begin + Result := (Self = Item.GetInstance); +end; + +function TAbstractMappable.GetKey: ICollectable; +begin + Result := FKey; +end; + +{ TAbstractStringMappable } +procedure TAbstractStringMappable.AfterConstruction; +begin + inherited AfterConstruction; + FKey := MakeKey; +end; + +function TAbstractStringMappable.Equals(const Item: ICollectable): Boolean; +begin + Result := (Self = Item.GetInstance); +end; + +function TAbstractStringMappable.GetKey: String; +begin + Result := FKey; +end; + +{ TAssociationWrapper } +constructor TAssociationWrapper.Create(const Key: ICollectable; Value: TObject); +begin + inherited Create; + FAutoDestroy := true; + FKey := Key; + FValue := Value; +end; + +constructor TAssociationWrapper.Create(Key: Integer; Value: TObject); +begin + Create(TIntegerWrapper.Create(Key) as ICollectable, Value); +end; + +constructor TAssociationWrapper.Create(Key: String; Value: TObject); +begin + Create(TStringWrapper.Create(Key) as ICollectable, Value); +end; + +constructor TAssociationWrapper.Create(Key, Value: TObject; AutoDestroyKey: Boolean); +var + KeyWrapper: TObjectWrapper; +begin + KeyWrapper := TObjectWrapper.Create(Key); + KeyWrapper.AutoDestroy := AutoDestroyKey; + Create(KeyWrapper as ICollectable, Value); +end; + +destructor TAssociationWrapper.Destroy; +begin + if FAutoDestroy then + FValue.Free; + FKey := nil; + inherited Destroy; +end; + +function TAssociationWrapper.GetAutoDestroy: Boolean; +begin + Result := FAutoDestroy; +end; + +procedure TAssociationWrapper.SetAutoDestroy(Value: Boolean); +begin + FAutoDestroy := Value; +end; + +function TAssociationWrapper.GetKey: ICollectable; +begin + Result := FKey; +end; + +function TAssociationWrapper.GetValue: TObject; +begin + Result := FValue; +end; + +function TAssociationWrapper.Equals(const Item: ICollectable): Boolean; +begin + Result := (Self.Value = (Item.GetInstance as TAssociationWrapper).Value) +end; + +{ TCardinalWrapper } +constructor TCardinalWrapper.Create(Value: Cardinal); +begin + inherited Create; + FValue := Value; +end; + +function TCardinalWrapper.GetValue: Cardinal; +begin + Result := FValue; +end; + +function TCardinalWrapper.Equals(const Item: ICollectable): Boolean; +begin + Result := (Self.Value = (Item.GetInstance as TCardinalWrapper).Value) +end; + +function TCardinalWrapper.HashCode: Integer; +begin + Result := FValue; +end; + +function TCardinalWrapper.CompareTo(const Item: ICollectable): Integer; +var + Value2: Cardinal; +begin + Value2 := (Item.GetInstance as TCardinalWrapper).Value; + if Value < Value2 then + Result := -1 + else if Value > Value2 then + Result := 1 + else + Result := 0; +end; + +{ TBooleanWrapper } +constructor TBooleanWrapper.Create(Value: Boolean); +begin + inherited Create; + FValue := Value; +end; + +function TBooleanWrapper.GetValue: Boolean; +begin + Result := FValue; +end; + +function TBooleanWrapper.Equals(const Item: ICollectable): Boolean; +begin + Result := (Self.Value = (Item.GetInstance as TBooleanWrapper).Value) +end; + +function TBooleanWrapper.HashCode: Integer; +begin + Result := Ord(FValue); +end; + +function TBooleanWrapper.CompareTo(const Item: ICollectable): Integer; +var + Value2: Boolean; +begin + Value2 := (Item.GetInstance as TBooleanWrapper).Value; + if not Value and Value2 then + Result := -1 + else if Value and not Value2 then + Result := 1 + else + Result := 0; +end; + +{ TCharWrapper } +constructor TCharWrapper.Create(Value: Char); +begin + inherited Create; + FValue := Value; +end; + +function TCharWrapper.GetValue: Char; +begin + Result := FValue; +end; + +function TCharWrapper.Equals(const Item: ICollectable): Boolean; +begin + Result := (Self.Value = (Item.GetInstance as TCharWrapper).Value) +end; + +function TCharWrapper.HashCode: Integer; +begin + Result := Integer(FValue); +end; + +function TCharWrapper.CompareTo(const Item: ICollectable): Integer; +var + Value2: Char; +begin + Value2 := (Item.GetInstance as TCharWrapper).Value; + if Value < Value2 then + Result := -1 + else if Value > Value2 then + Result := 1 + else + Result := 0; +end; + +{ TClassWrapper } +constructor TClassWrapper.Create(Value: TClass); +begin + inherited Create; + FValue := Value; +end; + +function TClassWrapper.GetValue: TClass; +begin + Result := FValue; +end; + +function TClassWrapper.Equals(const Item: ICollectable): Boolean; +begin + Result := (Self.Value = (Item.GetInstance as TClassWrapper).Value) +end; + +function TClassWrapper.HashCode: Integer; +begin + Result := Integer(FValue.ClassInfo); +end; + +{ TDoubleWrapper } +constructor TDoubleWrapper.Create(Value: Double); +begin + inherited Create; + FValue := Value; +end; + +function TDoubleWrapper.GetValue: Double; +begin + Result := FValue; +end; + +function TDoubleWrapper.Equals(const Item: ICollectable): Boolean; +begin + Result := (Self.Value = (Item.GetInstance as TDoubleWrapper).Value) +end; + +function TDoubleWrapper.HashCode: Integer; +var + DblAsInt: array[0..1] of Integer; +begin + Double(DblAsInt) := Value; + Result := DblAsInt[0] xor DblAsInt[1]; +end; + +function TDoubleWrapper.CompareTo(const Item: ICollectable): Integer; +var + Value2: Double; +begin + Value2 := (Item.GetInstance as TDoubleWrapper).Value; + if Value < Value2 then + Result := -1 + else if Value > Value2 then + Result := 1 + else + Result := 0; +end; + +{ TIntegerWrapper } +constructor TIntegerWrapper.Create(Value: Integer); +begin + inherited Create; + FValue := Value; +end; + +function TIntegerWrapper.GetValue: Integer; +begin + Result := FValue; +end; + +function TIntegerWrapper.Equals(const Item: ICollectable): Boolean; +begin + Result := (Self.Value = (Item.GetInstance as TIntegerWrapper).Value) +end; + +function TIntegerWrapper.HashCode: Integer; +begin + Result := FValue; +end; + +function TIntegerWrapper.CompareTo(const Item: ICollectable): Integer; +var + Value2: Integer; +begin + Value2 := (Item.GetInstance as TIntegerWrapper).Value; + if Value < Value2 then + Result := -1 + else if Value > Value2 then + Result := 1 + else + Result := 0; +end; + +{ TIntegerAssociationWrapper } +constructor TIntegerAssociationWrapper.Create(const Key: Integer; Value: TObject); +begin + inherited Create; + FAutoDestroy := true; + FKey := Key; + FValue := Value; +end; + +destructor TIntegerAssociationWrapper.Destroy; +begin + if FAutoDestroy then + FValue.Free; + inherited Destroy; +end; + +function TIntegerAssociationWrapper.GetAutoDestroy: Boolean; +begin + Result := FAutoDestroy; +end; + +procedure TIntegerAssociationWrapper.SetAutoDestroy(Value: Boolean); +begin + FAutoDestroy := Value; +end; + +function TIntegerAssociationWrapper.GetValue: TObject; +begin + Result := FValue; +end; + +function TIntegerAssociationWrapper.Equals(const Item: ICollectable): Boolean; +begin + Result := (Self.Value = (Item.GetInstance as TIntegerAssociationWrapper).Value) +end; + +function TIntegerAssociationWrapper.GetKey: Integer; +begin + Result := FKey; +end; + +{ TStringAssociationWrapper } +constructor TStringAssociationWrapper.Create(const Key: String; Value: TObject); +begin + inherited Create; + FAutoDestroy := true; + FKey := Key; + FValue := Value; +end; + +destructor TStringAssociationWrapper.Destroy; +begin + if FAutoDestroy then + FValue.Free; + inherited Destroy; +end; + +function TStringAssociationWrapper.GetAutoDestroy: Boolean; +begin + Result := FAutoDestroy; +end; + +procedure TStringAssociationWrapper.SetAutoDestroy(Value: Boolean); +begin + FAutoDestroy := Value; +end; + +function TStringAssociationWrapper.GetValue: TObject; +begin + Result := FValue; +end; + +function TStringAssociationWrapper.Equals(const Item: ICollectable): Boolean; +begin + Result := (Self.Value = (Item.GetInstance as TStringAssociationWrapper).Value) +end; + +function TStringAssociationWrapper.GetKey: String; +begin + Result := FKey; +end; + +{ TInterfaceWrapper } +constructor TInterfaceWrapper.Create(const Value: IUnknown); +begin + inherited Create; + FValue := Value; +end; + +destructor TInterfaceWrapper.Destroy; +begin + FValue := nil; + inherited Destroy; +end; + +function TInterfaceWrapper.GetValue: IUnknown; +begin + Result := FValue; +end; + +function TInterfaceWrapper.Equals(const Item: ICollectable): Boolean; +begin + Result := (Self.Value = (Item.GetInstance as TInterfaceWrapper).Value) +end; + +function TInterfaceWrapper.HashCode: Integer; +begin + Result := Integer(Pointer(FValue)); +end; + +{ TObjectWrapper } +constructor TObjectWrapper.Create(Value: TObject); +begin + inherited Create; + FAutoDestroy := true; + FValue := Value; +end; + +destructor TObjectWrapper.Destroy; +begin + if FAutoDestroy then + FValue.Free; + inherited Destroy; +end; + +function TObjectWrapper.GetAutoDestroy: Boolean; +begin + Result := FAutoDestroy; +end; + +procedure TObjectWrapper.SetAutoDestroy(Value: Boolean); +begin + FAutoDestroy := Value; +end; + +function TObjectWrapper.GetValue: TObject; +begin + Result := FValue; +end; + +function TObjectWrapper.CompareTo(const Item: ICollectable): Integer; +var + Value1, Value2: Integer; +begin + Value1 := Integer(Pointer(Self)); + if Item <> nil then + Value2 := Integer(Pointer(Item)) + else + Value2 := Low(Integer); + if (Value1 < Value2) then + Result := -1 + else if (Value1 > Value2) then + Result := 1 + else + Result := 0; +end; + +function TObjectWrapper.Equals(const Item: ICollectable): Boolean; +begin + Result := (Self.Value = (Item.GetInstance as TObjectWrapper).Value) +end; + +function TObjectWrapper.HashCode: Integer; +begin + Result := Integer(Pointer(FValue)); +end; + +{ TStringWrapper } +constructor TStringWrapper.Create(Value: String); +begin + inherited Create; + FValue := Value; +end; + +function TStringWrapper.GetValue: String; +begin + Result := FValue; +end; + +function TStringWrapper.Equals(const Item: ICollectable): Boolean; +begin + Result := (Self.Value = (Item.GetInstance as TStringWrapper).Value) +end; + +function TStringWrapper.HashCode: Integer; +var + I: Integer; +begin + Result := 0; + for I := 1 to Length(FValue) do + Result := (Result shl 1) xor Ord(FValue[I]); +end; + +function TStringWrapper.CompareTo(const Item: ICollectable): Integer; +begin + Result := CompareStr(Self.Value, (Item.GetInstance as TStringWrapper).Value) +end; + + +end. diff --git a/Lua/src/lib/collections/Collections.pas b/Lua/src/lib/collections/Collections.pas new file mode 100644 index 00000000..0c94173d --- /dev/null +++ b/Lua/src/lib/collections/Collections.pas @@ -0,0 +1,5318 @@ +unit Collections; +(***************************************************************************** + * Copyright 2003 by Matthew Greet + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * This library 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 Lesser General Public License for more + * details. (http://opensource.org/licenses/lgpl-license.php) + * + * See http://www.warmachine.u-net.com/delphi_collections for updates and downloads. + * + * $Version: v1.0 $ + * $Revision: 1.1.1.4 $ + * $Log: D:\QVCS Repositories\Delphi Collections\Collections.qbt $ + * + * Main unit containing all interface and abstract class definitions. + * + * Revision 1.1.1.4 by: Matthew Greet Rev date: 14/03/05 23:26:32 + * Fixed RemoveAll for TAbstractList for sorted lists. + * + * Revision 1.1.1.3 by: Matthew Greet Rev date: 14/10/04 16:31:18 + * Fixed memory lean in ContainsKey of TAbstractStringMap and + * TAbstractIntegerMap. + * + * Revision 1.1.1.2 by: Matthew Greet Rev date: 12/06/04 20:03:26 + * Capacity property. + * Memory leak fixed. + * + * Revision 1.1.1.1 by: Matthew Greet Rev date: 13/02/04 16:12:10 + * v1.0 branch. + * + * Revision 1.1 by: Matthew Greet Rev date: 06/04/03 10:36:30 + * Added integer map and string map collection types with supporting + * classes. + * Add clone and filter functions with supporting classes. + * Added nil not allowed collection error. + * Properties appear in collection interfaces as well as abstract + * classes. + * + * Revision 1.0 by: Matthew Greet Rev date: 01/03/03 10:50:02 + * Initial revision. + * + * FPC compatibility fixes by: UltraStar Deluxe Team + * + * $Endlog$ + *****************************************************************************) + +{$IFDEF FPC} + {$MODE Delphi}{$H+} +{$ENDIF} + +interface + +uses + Classes, SysUtils; + +const + EquatableIID: TGUID = '{EAC823A7-0B90-11D7-8120-0002E3165EF8}'; + HashableIID: TGUID = '{98998440-4C3E-11D7-8120-0002E3165EF8}'; + ComparableIID: TGUID = '{9F4C96C0-0CF0-11D7-8120-0002E3165EF8}'; + MappableIID: TGUID = '{DAEC8CA0-0DBB-11D7-8120-0002E3165EF8}'; + StringMappableIID: TGUID = '{3CC61F40-5F92-11D7-8120-0002E3165EF8}'; + IntegerMappableIID: TGUID = '{774FC760-5F92-11D7-8120-0002E3165EF8}'; + +type + TDefaultComparator = class; + TNaturalComparator = class; + ICollectable = interface; + + TCollectableArray = array of ICollectable; + TIntegerArray = array of Integer; + TStringArray = array of String; + TListArray = array of TList; + + TCollectionError = (ceOK, ceDuplicate, ceDuplicateKey, ceFixedSize, ceNilNotAllowed, ceNotNaturalItem, ceOutOfRange); + TCollectionErrors = set of TCollectionError; + + TSearchResultType = (srNotFound, srFoundAtIndex, srBeforeIndex, srAfterEnd); + + TCollectionType = (ctBag, ctSet, ctList, ctMap, ctIntegerMap, ctStringMap); + + TCollectionFilterFunc = function (const Item: ICollectable): Boolean of object; + TCollectionCompareFunc = function (const Item1, Item2: ICollectable): Integer of object; + + TSearchResult = record + ResultType: TSearchResultType; + Index: Integer; + end; + + ICollectable = interface + ['{98998441-4C3E-11D7-8120-0002E3165EF8}'] + function GetInstance: TObject; + end; + + IEquatable = interface + ['{EAC823A7-0B90-11D7-8120-0002E3165EF8}'] + function GetInstance: TObject; + function Equals(const Item: ICollectable): Boolean; + end; + + IHashable = interface(IEquatable) + ['{98998440-4C3E-11D7-8120-0002E3165EF8}'] + function HashCode: Integer; + end; + + IComparable = interface(IEquatable) + ['{9F4C96C0-0CF0-11D7-8120-0002E3165EF8}'] + function CompareTo(const Item: ICollectable): Integer; + end; + + IMappable = interface(IEquatable) + ['{DAEC8CA0-0DBB-11D7-8120-0002E3165EF8}'] + function GetKey: ICollectable; + end; + + IStringMappable = interface(IEquatable) + ['{3CC61F40-5F92-11D7-8120-0002E3165EF8}'] + function GetKey: String; + end; + + IIntegerMappable = interface(IEquatable) + ['{774FC760-5F92-11D7-8120-0002E3165EF8}'] + function GetKey: Integer; + end; + + IComparator = interface + ['{1F20CD60-10FE-11D7-8120-0002E3165EF8}'] + function GetInstance: TObject; + function Compare(const Item1, Item2: ICollectable): Integer; + function Equals(const Item1, Item2: ICollectable): Boolean; overload; + function Equals(const Comparator: IComparator): Boolean; overload; + end; + + IFilter = interface + ['{27FE44C0-638E-11D7-8120-0002E3165EF8}'] + function Accept(const Item: ICollectable): Boolean; + end; + + IIterator = interface + ['{F6930500-1113-11D7-8120-0002E3165EF8}'] + function GetAllowRemoval: Boolean; + function CurrentItem: ICollectable; + function EOF: Boolean; + function First: ICollectable; + function Next: ICollectable; + function Remove: Boolean; + end; + + IMapIterator = interface(IIterator) + ['{848CC0E0-2A31-11D7-8120-0002E3165EF8}'] + function CurrentKey: ICollectable; + end; + + IIntegerMapIterator = interface(IIterator) + ['{C7169780-606C-11D7-8120-0002E3165EF8}'] + function CurrentKey: Integer; + end; + + IStringMapIterator = interface(IIterator) + ['{1345ED20-5F93-11D7-8120-0002E3165EF8}'] + function CurrentKey: String; + end; + + IAssociation = interface(ICollectable) + ['{556CD700-4DB3-11D7-8120-0002E3165EF8}'] + function GetKey: ICollectable; + function GetValue: ICollectable; + end; + + IIntegerAssociation = interface(ICollectable) + ['{ED954420-5F94-11D7-8120-0002E3165EF8}'] + function GetKey: Integer; + function GetValue: ICollectable; + end; + + IStringAssociation = interface(ICollectable) + ['{FB87D2A0-5F94-11D7-8120-0002E3165EF8}'] + function GetKey: String; + function GetValue: ICollectable; + end; + + IAssociationComparator = interface(IComparator) + ['{EA9BE6E0-A852-11D8-B93A-0002E3165EF8}'] + function GetKeyComparator: IComparator; + procedure SetKeyComparator(Value: IComparator); + property KeyComparator: IComparator read GetKeyComparator write SetKeyComparator; + end; + + IIntegerAssociationComparator = interface(IComparator) + ['{EA9BE6E1-A852-11D8-B93A-0002E3165EF8}'] + end; + + IStringAssociationComparator = interface(IComparator) + ['{EA9BE6E2-A852-11D8-B93A-0002E3165EF8}'] + end; + + ICollection = interface + ['{EAC823AC-0B90-11D7-8120-0002E3165EF8}'] + function GetAsArray: TCollectableArray; + function GetCapacity: Integer; + procedure SetCapacity(Value: Integer); + function GetComparator: IComparator; + procedure SetComparator(const Value: IComparator); + function GetDuplicates: Boolean; + function GetFixedSize: Boolean; + function GetIgnoreErrors: TCollectionErrors; + procedure SetIgnoreErrors(Value: TCollectionErrors); + function GetInstance: TObject; + function GetIterator: IIterator; overload; + function GetIterator(const Filter: IFilter): IIterator; overload; + function GetIterator(FilterFunc: TCollectionFilterFunc): IIterator; overload; + function GetNaturalItemIID: TGUID; + function GetNaturalItemsOnly: Boolean; + function GetSize: Integer; + function GetType: TCollectionType; + function Add(const Item: ICollectable): Boolean; overload; + function Add(const ItemArray: array of ICollectable): Integer; overload; + function Add(const Collection: ICollection): Integer; overload; + function Clear: Integer; + function Clone: ICollection; + function Contains(const Item: ICollectable): Boolean; overload; + function Contains(const ItemArray: array of ICollectable): Boolean; overload; + function Contains(const Collection: ICollection): Boolean; overload; + function Equals(const Collection: ICollection): Boolean; + function Find(const Filter: IFilter): ICollectable; overload; + function Find(FilterFunc: TCollectionFilterFunc): ICollectable; overload; + function FindAll(const Filter: IFilter = nil): ICollection; overload; + function FindAll(FilterFunc: TCollectionFilterFunc): ICollection; overload; + function IsEmpty: Boolean; + function IsNaturalItem(const Item: ICollectable): Boolean; + function IsNilAllowed: Boolean; + function ItemAllowed(const Item: ICollectable): TCollectionError; + function ItemCount(const Item: ICollectable): Integer; overload; + function ItemCount(const ItemArray: array of ICollectable): Integer; overload; + function ItemCount(const Collection: ICollection): Integer; overload; + function Matching(const ItemArray: array of ICollectable): ICollection; overload; + function Matching(const Collection: ICollection): ICollection; overload; + function Remove(const Item: ICollectable): ICollectable; overload; + function Remove(const ItemArray: array of ICollectable): ICollection; overload; + function Remove(const Collection: ICollection): ICollection; overload; + function RemoveAll(const Item: ICollectable): ICollection; overload; + function RemoveAll(const ItemArray: array of ICollectable): ICollection; overload; + function RemoveAll(const Collection: ICollection): ICollection; overload; + function Retain(const ItemArray: array of ICollectable): ICollection; overload; + function Retain(const Collection: ICollection): ICollection; overload; + property AsArray: TCollectableArray read GetAsArray; + property Capacity: Integer read GetCapacity write SetCapacity; + property Comparator: IComparator read GetComparator write SetComparator; + property FixedSize: Boolean read GetFixedSize; + property IgnoreErrors: TCollectionErrors read GetIgnoreErrors write SetIgnoreErrors; + property NaturalItemIID: TGUID read GetNaturalItemIID; + property NaturalItemsOnly: Boolean read GetNaturalItemsOnly; + property Size: Integer read GetSize; + end; + + IBag = interface(ICollection) + ['{C29C9560-2D59-11D7-8120-0002E3165EF8}'] + function CloneAsBag: IBag; + end; + + ISet = interface(ICollection) + ['{DD7888E2-0BB1-11D7-8120-0002E3165EF8}'] + function CloneAsSet: ISet; + function Complement(const Universe: ISet): ISet; + function Intersect(const Set2: ISet): ISet; + function Union(const Set2: ISet): ISet; + end; + + IList = interface(ICollection) + ['{EE81AB60-0B9F-11D7-8120-0002E3165EF8}'] + function GetDuplicates: Boolean; + procedure SetDuplicates(Value: Boolean); + function GetItem(Index: Integer): ICollectable; + procedure SetItem(Index: Integer; const Item: ICollectable); + function GetSorted: Boolean; + procedure SetSorted(Value: Boolean); + function CloneAsList: IList; + function Delete(Index: Integer): ICollectable; + procedure Exchange(Index1, Index2: Integer); + function First: ICollectable; + function IndexOf(const Item: ICollectable): Integer; + function Insert(Index: Integer; const Item: ICollectable): Boolean; overload; + function Insert(Index: Integer; const ItemArray: array of ICollectable): Integer; overload; + function Insert(Index: Integer; const Collection: ICollection): Integer; overload; + function Last: ICollectable; + procedure Sort(const Comparator: IComparator); overload; + procedure Sort(CompareFunc: TCollectionCompareFunc); overload; + property Duplicates: Boolean read GetDuplicates write SetDuplicates; + property Items[Index: Integer]: ICollectable read GetItem write SetItem; default; + property Sorted: Boolean read GetSorted write SetSorted; + end; + + IMap = interface(ICollection) + ['{AD458280-2A6B-11D7-8120-0002E3165EF8}'] + function GetItem(const Key: ICollectable): ICollectable; + procedure SetItem(const Key, Item: ICollectable); + function GetKeyComparator: IComparator; + procedure SetKeyComparator(const Value: IComparator); + function GetKeyIterator: IIterator; + function GetKeys: ISet; + function GetMapIterator: IMapIterator; + function GetMapIteratorByKey(const Filter: IFilter): IMapIterator; overload; + function GetMapIteratorByKey(FilterFunc: TCollectionFilterFunc): IMapIterator; overload; + function GetNaturalKeyIID: TGUID; + function GetNaturalKeysOnly: Boolean; + function GetValues: ICollection; + function CloneAsMap: IMap; + function ContainsKey(const Key: ICollectable): Boolean; overload; + function ContainsKey(const KeyArray: array of ICollectable): Boolean; overload; + function ContainsKey(const Collection: ICollection): Boolean; overload; + function Get(const Key: ICollectable): ICollectable; + function IsNaturalKey(const Key: ICollectable): Boolean; + function KeyAllowed(const Key: ICollectable): TCollectionError; + function MatchingKey(const KeyArray: array of ICollectable): ICollection; overload; + function MatchingKey(const Collection: ICollection): ICollection; overload; + function Put(const Item: ICollectable): ICollectable; overload; + function Put(const Key, Item: ICollectable): ICollectable; overload; + function Put(const ItemArray: array of ICollectable): ICollection; overload; + function Put(const Collection: ICollection): ICollection; overload; + function Put(const Map: IMap): ICollection; overload; + function RemoveKey(const Key: ICollectable): ICollectable; overload; + function RemoveKey(const KeyArray: array of ICollectable): ICollection; overload; + function RemoveKey(const Collection: ICollection): ICollection; overload; + function RetainKey(const KeyArray: array of ICollectable): ICollection; overload; + function RetainKey(const Collection: ICollection): ICollection; overload; + property KeyComparator: IComparator read GetKeyComparator write SetKeyComparator; + property Items[const Key: ICollectable]: ICollectable read GetItem write SetItem; default; + property NaturalKeyIID: TGUID read GetNaturalKeyIID; + property NaturalKeysOnly: Boolean read GetNaturalKeysOnly; + end; + + IIntegerMap = interface(ICollection) + ['{93DBA9A0-606C-11D7-8120-0002E3165EF8}'] + function GetItem(const Key: Integer): ICollectable; + procedure SetItem(const Key: Integer; const Item: ICollectable); + function GetKeys: ISet; + function GetMapIterator: IIntegerMapIterator; + function GetValues: ICollection; + function CloneAsIntegerMap: IIntegerMap; + function ContainsKey(const Key: Integer): Boolean; overload; + function ContainsKey(const KeyArray: array of Integer): Boolean; overload; + function Get(const Key: Integer): ICollectable; + function Put(const Item: ICollectable): ICollectable; overload; + function Put(const Key: Integer; const Item: ICollectable): ICollectable; overload; + function Put(const ItemArray: array of ICollectable): ICollection; overload; + function Put(const Collection: ICollection): ICollection; overload; + function Put(const Map: IIntegerMap): ICollection; overload; + function RemoveKey(const Key: Integer): ICollectable; overload; + function RemoveKey(const KeyArray: array of Integer): ICollection; overload; + function RetainKey(const KeyArray: array of Integer): ICollection; overload; + property Items[const Key: Integer]: ICollectable read GetItem write SetItem; default; + end; + + IStringMap = interface(ICollection) + ['{20531A20-5F92-11D7-8120-0002E3165EF8}'] + function GetItem(const Key: String): ICollectable; + procedure SetItem(const Key: String; const Item: ICollectable); + function GetKeys: ISet; + function GetMapIterator: IStringMapIterator; + function GetValues: ICollection; + function CloneAsStringMap: IStringMap; + function ContainsKey(const Key: String): Boolean; overload; + function ContainsKey(const KeyArray: array of String): Boolean; overload; + function Get(const Key: String): ICollectable; + function Put(const Item: ICollectable): ICollectable; overload; + function Put(const Key: String; const Item: ICollectable): ICollectable; overload; + function Put(const ItemArray: array of ICollectable): ICollection; overload; + function Put(const Collection: ICollection): ICollection; overload; + function Put(const Map: IStringMap): ICollection; overload; + function RemoveKey(const Key: String): ICollectable; overload; + function RemoveKey(const KeyArray: array of String): ICollection; overload; + function RetainKey(const KeyArray: array of String): ICollection; overload; + property Items[const Key: String]: ICollectable read GetItem write SetItem; default; + end; + + TCollectionPosition = class + private + FFound: Boolean; + public + constructor Create(Found: Boolean); + property Found: Boolean read FFound; + end; + + TAbstractComparator = class(TInterfacedObject, IComparator) + public + class function GetDefaultComparator: IComparator; + class function GetNaturalComparator: IComparator; + class function GetReverseNaturalComparator: IComparator; + function GetInstance: TObject; + function Compare(const Item1, Item2: ICollectable): Integer; virtual; abstract; + function Equals(const Item1, Item2: ICollectable): Boolean; overload; virtual; abstract; + function Equals(const Comparator: IComparator): Boolean; overload; virtual; + end; + + TDefaultComparator = class(TAbstractComparator) + protected + constructor Create; + public + function Compare(const Item1, Item2: ICollectable): Integer; override; + function Equals(const Item1, Item2: ICollectable): Boolean; override; + end; + + TNaturalComparator = class(TAbstractComparator) + protected + constructor Create; + public + function Compare(const Item1, Item2: ICollectable): Integer; override; + function Equals(const Item1, Item2: ICollectable): Boolean; override; + end; + + TReverseNaturalComparator = class(TAbstractComparator) + protected + constructor Create; + public + function Compare(const Item1, Item2: ICollectable): Integer; override; + function Equals(const Item1, Item2: ICollectable): Boolean; override; + end; + + TAssociation = class(TInterfacedObject, ICollectable, IAssociation) + private + FKey: ICollectable; + FValue: ICollectable; + public + constructor Create(const Key, Value: ICollectable); virtual; + destructor Destroy; override; + function GetInstance: TObject; virtual; + function GetKey: ICollectable; + function GetValue: ICollectable; + end; + + TIntegerAssociation = class(TInterfacedObject, ICollectable, IIntegerAssociation) + private + FKey: Integer; + FValue: ICollectable; + public + constructor Create(const Key: Integer; const Value: ICollectable); virtual; + destructor Destroy; override; + function GetInstance: TObject; virtual; + function GetKey: Integer; + function GetValue: ICollectable; + end; + + TStringAssociation = class(TInterfacedObject, ICollectable, IStringAssociation) + private + FKey: String; + FValue: ICollectable; + public + constructor Create(const Key: String; const Value: ICollectable); virtual; + destructor Destroy; override; + function GetInstance: TObject; virtual; + function GetKey: String; + function GetValue: ICollectable; + end; + + TAssociationComparator = class(TAbstractComparator, IAssociationComparator) + private + FKeyComparator: IComparator; + public + constructor Create(NaturalKeys: Boolean = false); + destructor Destroy; override; + function GetKeyComparator: IComparator; + procedure SetKeyComparator(Value: IComparator); + function Compare(const Item1, Item2: ICollectable): Integer; override; + function Equals(const Item1, Item2: ICollectable): Boolean; override; + property KeyComparator: IComparator read GetKeyComparator write SetKeyComparator; + end; + + TIntegerAssociationComparator = class(TAbstractComparator, IIntegerAssociationComparator) + public + constructor Create; + destructor Destroy; override; + function Compare(const Item1, Item2: ICollectable): Integer; override; + function Equals(const Item1, Item2: ICollectable): Boolean; override; + end; + + TStringAssociationComparator = class(TAbstractComparator, IStringAssociationComparator) + public + constructor Create; + destructor Destroy; override; + function Compare(const Item1, Item2: ICollectable): Integer; override; + function Equals(const Item1, Item2: ICollectable): Boolean; override; + end; + + + + TAbstractCollection = class(TInterfacedObject, ICollection) + private + FCreated: Boolean; // Required to avoid passing destroyed object reference to exception + FComparator: IComparator; + FIgnoreErrors: TCollectionErrors; + FNaturalItemsOnly: Boolean; + protected + procedure CollectionError(ErrorType: TCollectionError); + procedure InitFrom(const Collection: ICollection); overload; virtual; + function TrueAdd(const Item: ICollectable): Boolean; virtual; abstract; + procedure TrueClear; virtual; abstract; + function TrueContains(const Item: ICollectable): Boolean; virtual; abstract; + function TrueItemCount(const Item: ICollectable): Integer; virtual; + function TrueRemove(const Item: ICollectable): ICollectable; virtual; abstract; + function TrueRemoveAll(const Item: ICollectable): ICollection; virtual; abstract; + public + constructor Create; overload; virtual; + constructor Create(NaturalItemsOnly: Boolean); overload; virtual; + constructor Create(const ItemArray: array of ICollectable); overload; virtual; + constructor Create(const ItemArray: array of ICollectable; NaturalItemsOnly: Boolean); overload; virtual; + constructor Create(const Collection: ICollection); overload; virtual; + destructor Destroy; override; + class function GetAlwaysNaturalItems: Boolean; virtual; + function GetAsArray: TCollectableArray; virtual; + function GetCapacity: Integer; virtual; abstract; + procedure SetCapacity(Value: Integer); virtual; abstract; + function GetComparator: IComparator; virtual; + procedure SetComparator(const Value: IComparator); virtual; + function GetDuplicates: Boolean; virtual; + function GetFixedSize: Boolean; virtual; + function GetIgnoreErrors: TCollectionErrors; + procedure SetIgnoreErrors(Value: TCollectionErrors); + function GetInstance: TObject; + function GetIterator: IIterator; overload; virtual; abstract; + function GetIterator(const Filter: IFilter): IIterator; overload; virtual; + function GetIterator(FilterFunc: TCollectionFilterFunc): IIterator; overload; virtual; + function GetNaturalItemIID: TGUID; virtual; abstract; + function GetNaturalItemsOnly: Boolean; virtual; + function GetSize: Integer; virtual; abstract; + function GetType: TCollectionType; virtual; abstract; + function Add(const Item: ICollectable): Boolean; overload; virtual; + function Add(const ItemArray: array of ICollectable): Integer; overload; virtual; + function Add(const Collection: ICollection): Integer; overload; virtual; + procedure AfterConstruction; override; + procedure BeforeDestruction; override; + function Clear: Integer; virtual; + function Clone: ICollection; virtual; + function Contains(const Item: ICollectable): Boolean; overload; virtual; + function Contains(const ItemArray: array of ICollectable): Boolean; overload; virtual; + function Contains(const Collection: ICollection): Boolean; overload; virtual; + function Equals(const Collection: ICollection): Boolean; virtual; + function Find(const Filter: IFilter): ICollectable; overload; virtual; + function Find(FilterFunc: TCollectionFilterFunc): ICollectable; overload; virtual; + function FindAll(const Filter: IFilter): ICollection; overload; virtual; + function FindAll(FilterFunc: TCollectionFilterFunc): ICollection; overload; virtual; + function IsEmpty: Boolean; virtual; + function IsNaturalItem(const Item: ICollectable): Boolean; virtual; + function IsNilAllowed: Boolean; virtual; abstract; + function ItemAllowed(const Item: ICollectable): TCollectionError; virtual; + function ItemCount(const Item: ICollectable): Integer; overload; virtual; + function ItemCount(const ItemArray: array of ICollectable): Integer; overload; virtual; + function ItemCount(const Collection: ICollection): Integer; overload; virtual; + function Matching(const ItemArray: array of ICollectable): ICollection; overload; virtual; + function Matching(const Collection: ICollection): ICollection; overload; virtual; + function Remove(const Item: ICollectable): ICollectable; overload; virtual; + function Remove(const ItemArray: array of ICollectable): ICollection; overload; virtual; + function Remove(const Collection: ICollection): ICollection; overload; virtual; + function RemoveAll(const Item: ICollectable): ICollection; overload; virtual; + function RemoveAll(const ItemArray: array of ICollectable): ICollection; overload; virtual; + function RemoveAll(const Collection: ICollection): ICollection; overload; virtual; + function Retain(const ItemArray: array of ICollectable): ICollection; overload; virtual; + function Retain(const Collection: ICollection): ICollection; overload; virtual; + property AsArray: TCollectableArray read GetAsArray; + property Capacity: Integer read GetCapacity write SetCapacity; + property Comparator: IComparator read GetComparator write SetComparator; + property FixedSize: Boolean read GetFixedSize; + property IgnoreErrors: TCollectionErrors read GetIgnoreErrors write SetIgnoreErrors; + property NaturalItemIID: TGUID read GetNaturalItemIID; + property NaturalItemsOnly: Boolean read GetNaturalItemsOnly; + property Size: Integer read GetSize; + end; + + TAbstractBag = class(TAbstractCollection, IBag) + public + function CloneAsBag: IBag; virtual; + function GetNaturalItemIID: TGUID; override; + function GetType: TCollectionType; override; + function IsNilAllowed: Boolean; override; + end; + + TAbstractSet = class (TAbstractCollection, ISet) + protected + function GetPosition(const Item: ICollectable): TCollectionPosition; virtual; abstract; + function TrueAdd(const Item: ICollectable): Boolean; override; + procedure TrueAdd2(Position: TCollectionPosition; const Item: ICollectable); virtual; abstract; + function TrueContains(const Item: ICollectable): Boolean; override; + function TrueGet(Position: TCollectionPosition): ICollectable; virtual; abstract; + function TrueRemove(const Item: ICollectable): ICollectable; override; + procedure TrueRemove2(Position: TCollectionPosition); virtual; abstract; + function TrueRemoveAll(const Item: ICollectable): ICollection; override; + public + function GetDuplicates: Boolean; override; + function GetNaturalItemIID: TGUID; override; + function GetType: TCollectionType; override; + function CloneAsSet: ISet; virtual; + function Complement(const Universe: ISet): ISet; overload; virtual; + function Intersect(const Set2: ISet): ISet; overload; virtual; + function IsNilAllowed: Boolean; override; + function Union(const Set2: ISet): ISet; overload; virtual; + end; + + TAbstractList = class(TAbstractCollection, IList) + private + FDuplicates: Boolean; + FSorted: Boolean; + protected + function BinarySearch(const Item: ICollectable): TSearchResult; virtual; + procedure InitFrom(const Collection: ICollection); override; + procedure QuickSort(Lo, Hi: Integer; const Comparator: IComparator); overload; virtual; + procedure QuickSort(Lo, Hi: Integer; CompareFunc: TCollectionCompareFunc); overload; virtual; + function SequentialSearch(const Item: ICollectable; const SearchComparator: IComparator = nil): TSearchResult; virtual; + function TrueContains(const Item: ICollectable): Boolean; override; + function TrueGetItem(Index: Integer): ICollectable; virtual; abstract; + procedure TrueSetItem(Index: Integer; const Item: ICollectable); virtual; abstract; + function TrueAdd(const Item: ICollectable): Boolean; override; + procedure TrueAppend(const Item: ICollectable); virtual; abstract; + function TrueDelete(Index: Integer): ICollectable; virtual; abstract; + procedure TrueInsert(Index: Integer; const Item: ICollectable); virtual; abstract; + function TrueItemCount(const Item: ICollectable): Integer; override; + function TrueRemove(const Item: ICollectable): ICollectable; override; + function TrueRemoveAll(const Item: ICollectable): ICollection; override; + public + constructor Create(NaturalItemsOnly: Boolean); override; + function GetDuplicates: Boolean; override; + procedure SetDuplicates(Value: Boolean); virtual; + function GetItem(Index: Integer): ICollectable; virtual; + procedure SetItem(Index: Integer; const Item: ICollectable); virtual; + function GetIterator: IIterator; override; + function GetNaturalItemIID: TGUID; override; + function GetSorted: Boolean; virtual; + procedure SetSorted(Value: Boolean); virtual; + function GetType: TCollectionType; override; + function CloneAsList: IList; virtual; + function Delete(Index: Integer): ICollectable; virtual; + procedure Exchange(Index1, Index2: Integer); virtual; + function First: ICollectable; virtual; + function IndexOf(const Item: ICollectable): Integer; virtual; + function Insert(Index: Integer; const Item: ICollectable): Boolean; overload; virtual; + function Insert(Index: Integer; const ItemArray: array of ICollectable): Integer; overload; virtual; + function Insert(Index: Integer; const Collection: ICollection): Integer; overload; virtual; + function IsNilAllowed: Boolean; override; + function Last: ICollectable; virtual; + function Search(const Item: ICollectable; const SearchComparator: IComparator = nil): TSearchResult; virtual; + procedure Sort(const SortComparator: IComparator = nil); overload; virtual; + procedure Sort(CompareFunc: TCollectionCompareFunc); overload; virtual; + property Duplicates: Boolean read GetDuplicates write SetDuplicates; + property Items[Index: Integer]: ICollectable read GetItem write SetItem; default; + property Sorted: Boolean read GetSorted write SetSorted; + end; + + TAbstractMap = class(TAbstractCollection, IMap) + private + FAssociationComparator: IAssociationComparator; + FKeyComparator: IComparator; + FNaturalKeysOnly: Boolean; + protected + function GetAssociationIterator: IMapIterator; virtual; abstract; + function GetKeyPosition(const Key: ICollectable): TCollectionPosition; virtual; abstract; + procedure InitFrom(const Collection: ICollection); override; + function TrueAdd(const Item: ICollectable): Boolean; override; + function TrueContains(const Item: ICollectable): Boolean; override; + function TrueGet(Position: TCollectionPosition): IAssociation; virtual; abstract; + function TruePut(Position: TCollectionPosition; const Association: IAssociation): IAssociation; virtual; abstract; + function TrueRemove(const Item: ICollectable): ICollectable; override; + function TrueRemove2(Position: TCollectionPosition): IAssociation; virtual; abstract; + function TrueRemoveAll(const Item: ICollectable): ICollection; override; + property AssociationComparator: IAssociationComparator read FAssociationComparator; + public + constructor Create; override; + constructor Create(NaturalItemsOnly: Boolean); override; + constructor Create(NaturalItemsOnly: Boolean; NaturalKeysOnly: Boolean); overload; virtual; + constructor Create(const ItemArray: array of ICollectable); overload; override; + constructor Create(const ItemArray: array of ICollectable; NaturalItemsOnly: Boolean); overload; override; + constructor Create(const ItemArray: array of ICollectable; NaturalItemsOnly: Boolean; NaturalKeysOnly: Boolean); overload; virtual; + constructor Create(const KeyArray, ItemArray: array of ICollectable); overload; virtual; + constructor Create(const KeyArray, ItemArray: array of ICollectable; NaturalItemsOnly: Boolean); overload; virtual; + constructor Create(const KeyArray, ItemArray: array of ICollectable; NaturalItemsOnly: Boolean; NaturalKeysOnly: Boolean); overload; virtual; +// Don't use this parameter signature as it hits a compiler bug in D5. +// constructor Create(const KeyArray, ItemArray: TCollectableArray; NaturalItemsOnly: Boolean = false; NaturalKeysOnly: Boolean = true); overload; virtual; + constructor Create(const Map: IMap); overload; virtual; + destructor Destroy; override; + class function GetAlwaysNaturalKeys: Boolean; virtual; + function GetItem(const Key: ICollectable): ICollectable; virtual; + procedure SetItem(const Key, Item: ICollectable); virtual; + function GetIterator: IIterator; override; + function GetKeyComparator: IComparator; virtual; + procedure SetKeyComparator(const Value: IComparator); virtual; + function GetKeyIterator: IIterator; virtual; + function GetKeys: ISet; virtual; + function GetMapIterator: IMapIterator; virtual; + function GetMapIteratorByKey(const Filter: IFilter): IMapIterator; overload; virtual; + function GetMapIteratorByKey(FilterFunc: TCollectionFilterFunc): IMapIterator; overload; virtual; + function GetNaturalItemIID: TGUID; override; + function GetNaturalKeyIID: TGUID; virtual; + function GetNaturalKeysOnly: Boolean; virtual; + function GetType: TCollectionType; override; + function GetValues: ICollection; virtual; + function Clone: ICollection; override; + function CloneAsMap: IMap; virtual; + function ContainsKey(const Key: ICollectable): Boolean; overload; virtual; + function ContainsKey(const KeyArray: array of ICollectable): Boolean; overload; virtual; + function ContainsKey(const Collection: ICollection): Boolean; overload; virtual; + function Get(const Key: ICollectable): ICollectable; virtual; + function KeyAllowed(const Key: ICollectable): TCollectionError; virtual; + function IsNaturalKey(const Key: ICollectable): Boolean; virtual; + function IsNilAllowed: Boolean; override; + function MatchingKey(const KeyArray: array of ICollectable): ICollection; overload; virtual; + function MatchingKey(const Collection: ICollection): ICollection; overload; virtual; + function Put(const Item: ICollectable): ICollectable; overload; virtual; + function Put(const Key, Item: ICollectable): ICollectable; overload; virtual; + function Put(const ItemArray: array of ICollectable): ICollection; overload; virtual; + function Put(const Collection: ICollection): ICollection; overload; virtual; + function Put(const Map: IMap): ICollection; overload; virtual; + function RemoveKey(const Key: ICollectable): ICollectable; overload; virtual; + function RemoveKey(const KeyArray: array of ICollectable): ICollection; overload; virtual; + function RemoveKey(const Collection: ICollection): ICollection; overload; virtual; + function RetainKey(const KeyArray: array of ICollectable): ICollection; overload; virtual; + function RetainKey(const Collection: ICollection): ICollection; overload; virtual; + property KeyComparator: IComparator read GetKeyComparator write SetKeyComparator; + property Items[const Key: ICollectable]: ICollectable read GetItem write SetItem; default; + property NaturalKeyIID: TGUID read GetNaturalKeyIID; + property NaturalKeysOnly: Boolean read GetNaturalKeysOnly; + end; + + TAbstractIntegerMap = class(TAbstractCollection, IIntegerMap) + private + FAssociationComparator: IIntegerAssociationComparator; + protected + function GetAssociationIterator: IIntegerMapIterator; virtual; abstract; + function GetKeyPosition(const Key: Integer): TCollectionPosition; virtual; abstract; + function TrueAdd(const Item: ICollectable): Boolean; override; + function TrueContains(const Item: ICollectable): Boolean; override; + function TrueGet(Position: TCollectionPosition): IIntegerAssociation; virtual; abstract; + function TruePut(Position: TCollectionPosition; const Association: IIntegerAssociation): IIntegerAssociation; virtual; abstract; + function TrueRemove(const Item: ICollectable): ICollectable; override; + function TrueRemove2(Position: TCollectionPosition): IIntegerAssociation; virtual; abstract; + function TrueRemoveAll(const Item: ICollectable): ICollection; override; + property AssociationComparator: IIntegerAssociationComparator read FAssociationComparator; + public + constructor Create(NaturalItemsOnly: Boolean); override; + constructor Create(const ItemArray: array of ICollectable); overload; override; + constructor Create(const ItemArray: array of ICollectable; NaturalItemsOnly: Boolean); overload; override; + constructor Create(const KeyArray: array of Integer; const ItemArray: array of ICollectable); overload; virtual; + constructor Create(const KeyArray: array of Integer; const ItemArray: array of ICollectable; NaturalItemsOnly: Boolean); overload; virtual; + constructor Create(const Map: IIntegerMap); overload; virtual; + destructor Destroy; override; + function GetItem(const Key: Integer): ICollectable; virtual; + procedure SetItem(const Key: Integer; const Item: ICollectable); virtual; + function GetIterator: IIterator; override; + function GetKeys: ISet; virtual; + function GetMapIterator: IIntegerMapIterator; virtual; + function GetNaturalItemIID: TGUID; override; + function GetType: TCollectionType; override; + function GetValues: ICollection; virtual; + function Clone: ICollection; override; + function CloneAsIntegerMap: IIntegerMap; virtual; + function ContainsKey(const Key: Integer): Boolean; overload; virtual; + function ContainsKey(const KeyArray: array of Integer): Boolean; overload; virtual; + function Get(const Key: Integer): ICollectable; virtual; + function IsNilAllowed: Boolean; override; + function Put(const Item: ICollectable): ICollectable; overload; virtual; + function Put(const Key: Integer; const Item: ICollectable): ICollectable; overload; virtual; + function Put(const ItemArray: array of ICollectable): ICollection; overload; virtual; + function Put(const Collection: ICollection): ICollection; overload; virtual; + function Put(const Map: IIntegerMap): ICollection; overload; virtual; + function RemoveKey(const Key: Integer): ICollectable; overload; virtual; + function RemoveKey(const KeyArray: array of Integer): ICollection; overload; virtual; + function RetainKey(const KeyArray: array of Integer): ICollection; overload; virtual; + property Items[const Key: Integer]: ICollectable read GetItem write SetItem; default; + end; + + TAbstractStringMap = class(TAbstractCollection, IStringMap) + private + FAssociationComparator: IStringAssociationComparator; + protected + function GetAssociationIterator: IStringMapIterator; virtual; abstract; + function GetKeyPosition(const Key: String): TCollectionPosition; virtual; abstract; + function TrueAdd(const Item: ICollectable): Boolean; override; + function TrueContains(const Item: ICollectable): Boolean; override; + function TrueGet(Position: TCollectionPosition): IStringAssociation; virtual; abstract; + function TruePut(Position: TCollectionPosition; const Association: IStringAssociation): IStringAssociation; virtual; abstract; + function TrueRemove(const Item: ICollectable): ICollectable; override; + function TrueRemove2(Position: TCollectionPosition): IStringAssociation; virtual; abstract; + function TrueRemoveAll(const Item: ICollectable): ICollection; override; + property AssociationComparator: IStringAssociationComparator read FAssociationComparator; + public + constructor Create(NaturalItemsOnly: Boolean); override; + constructor Create(const ItemArray: array of ICollectable); overload; override; + constructor Create(const ItemArray: array of ICollectable; NaturalItemsOnly: Boolean); overload; override; + constructor Create(const KeyArray: array of String; const ItemArray: array of ICollectable); overload; virtual; + constructor Create(const KeyArray: array of String; const ItemArray: array of ICollectable; NaturalItemsOnly: Boolean); overload; virtual; + constructor Create(const Map: IStringMap); overload; virtual; + destructor Destroy; override; + function GetItem(const Key: String): ICollectable; virtual; + procedure SetItem(const Key: String; const Item: ICollectable); virtual; + function GetIterator: IIterator; override; + function GetKeys: ISet; virtual; + function GetMapIterator: IStringMapIterator; virtual; + function GetNaturalItemIID: TGUID; override; + function GetType: TCollectionType; override; + function GetValues: ICollection; virtual; + function Clone: ICollection; override; + function CloneAsStringMap: IStringMap; virtual; + function ContainsKey(const Key: String): Boolean; overload; virtual; + function ContainsKey(const KeyArray: array of String): Boolean; overload; virtual; + function Get(const Key: String): ICollectable; virtual; + function IsNilAllowed: Boolean; override; + function Put(const Item: ICollectable): ICollectable; overload; virtual; + function Put(const Key: String; const Item: ICollectable): ICollectable; overload; virtual; + function Put(const ItemArray: array of ICollectable): ICollection; overload; virtual; + function Put(const Collection: ICollection): ICollection; overload; virtual; + function Put(const Map: IStringMap): ICollection; overload; virtual; + function RemoveKey(const Key: String): ICollectable; overload; virtual; + function RemoveKey(const KeyArray: array of String): ICollection; overload; virtual; + function RetainKey(const KeyArray: array of String): ICollection; overload; virtual; + property Items[const Key: String]: ICollectable read GetItem write SetItem; default; + end; + + TAbstractCollectionClass = class of TAbstractCollection; + TAbstractBagClass = class of TAbstractBag; + TAbstractSetClass = class of TAbstractSet; + TAbstractListClass = class of TAbstractList; + TAbstractMapClass = class of TAbstractMap; + TAbstractIntegerMapClass = class of TAbstractIntegerMap; + TAbstractStringMapClass = class of TAbstractStringMap; + + TAbstractIterator = class(TInterfacedObject, IIterator) + private + FAllowRemoval: Boolean; + FEOF: Boolean; + FItem: ICollectable; + protected + constructor Create(AllowRemoval: Boolean = true); + function TrueFirst: ICollectable; virtual; abstract; + function TrueNext: ICollectable; virtual; abstract; + procedure TrueRemove; virtual; abstract; + public + procedure AfterConstruction; override; + function GetAllowRemoval: Boolean; virtual; + function CurrentItem: ICollectable; virtual; + function EOF: Boolean; virtual; + function First: ICollectable; virtual; + function Next: ICollectable; virtual; + function Remove: Boolean; virtual; + property AllowRemoval: Boolean read GetAllowRemoval; + end; + + TAbstractListIterator = class(TAbstractIterator) + private + FCollection: TAbstractList; + FIndex: Integer; + protected + constructor Create(Collection: TAbstractList); + function TrueFirst: ICollectable; override; + function TrueNext: ICollectable; override; + procedure TrueRemove; override; + end; + + TAbstractMapIterator = class(TAbstractIterator, IMapIterator) + public + function CurrentKey: ICollectable; virtual; abstract; + end; + + TAbstractAssociationIterator = class(TInterfacedObject, IIterator, IMapIterator) + private + FAllowRemoval: Boolean; + FEOF: Boolean; + FAssociation: IAssociation; + protected + constructor Create(AllowRemoval: Boolean = true); + function TrueFirst: IAssociation; virtual; abstract; + function TrueNext: IAssociation; virtual; abstract; + procedure TrueRemove; virtual; abstract; + public + procedure AfterConstruction; override; + function GetAllowRemoval: Boolean; virtual; + function CurrentKey: ICollectable; virtual; + function CurrentItem: ICollectable; virtual; + function EOF: Boolean; virtual; + function First: ICollectable; virtual; + function Next: ICollectable; virtual; + function Remove: Boolean; virtual; + property AllowRemoval: Boolean read GetAllowRemoval; + end; + + TAbstractIntegerAssociationIterator = class(TInterfacedObject, IIterator, IIntegerMapIterator) + private + FAllowRemoval: Boolean; + FEOF: Boolean; + FAssociation: IIntegerAssociation; + protected + constructor Create(AllowRemoval: Boolean = true); + function TrueFirst: IIntegerAssociation; virtual; abstract; + function TrueNext: IIntegerAssociation; virtual; abstract; + procedure TrueRemove; virtual; abstract; + public + procedure AfterConstruction; override; + function GetAllowRemoval: Boolean; virtual; + function CurrentKey: Integer; virtual; + function CurrentItem: ICollectable; virtual; + function EOF: Boolean; virtual; + function First: ICollectable; virtual; + function Next: ICollectable; virtual; + function Remove: Boolean; virtual; + property AllowRemoval: Boolean read GetAllowRemoval; + end; + + TAbstractStringAssociationIterator = class(TInterfacedObject, IIterator, IStringMapIterator) + private + FAllowRemoval: Boolean; + FEOF: Boolean; + FAssociation: IStringAssociation; + protected + constructor Create(AllowRemoval: Boolean = true); + function TrueFirst: IStringAssociation; virtual; abstract; + function TrueNext: IStringAssociation; virtual; abstract; + procedure TrueRemove; virtual; abstract; + public + procedure AfterConstruction; override; + function GetAllowRemoval: Boolean; virtual; + function CurrentKey: String; virtual; + function CurrentItem: ICollectable; virtual; + function EOF: Boolean; virtual; + function First: ICollectable; virtual; + function Next: ICollectable; virtual; + function Remove: Boolean; virtual; + property AllowRemoval: Boolean read GetAllowRemoval; + end; + + TAssociationIterator = class(TAbstractIterator, IMapIterator) + private + FIterator: IIterator; + protected + function TrueFirst: ICollectable; override; + function TrueNext: ICollectable; override; + procedure TrueRemove; override; + public + constructor Create(const Iterator: IIterator); + destructor Destroy; override; + function CurrentItem: ICollectable; override; + function CurrentKey: ICollectable; virtual; + end; + + TAssociationKeyIterator = class(TAbstractIterator) + private + FIterator: IMapIterator; + protected + function TrueFirst: ICollectable; override; + function TrueNext: ICollectable; override; + procedure TrueRemove; override; + public + constructor Create(const Iterator: IMapIterator); + destructor Destroy; override; + end; + + TAbstractFilter = class(TInterfacedObject, IFilter) + public + function Accept(const Item: ICollectable): Boolean; virtual; abstract; + end; + + TFilterIterator = class(TAbstractIterator) + private + FIterator: IIterator; + FFilter: IFilter; + protected + function TrueFirst: ICollectable; override; + function TrueNext: ICollectable; override; + procedure TrueRemove; override; + public + constructor Create(const Iterator: IIterator; const Filter: IFilter; AllowRemoval: Boolean = true); virtual; + destructor Destroy; override; + end; + + TFilterFuncIterator = class(TAbstractIterator) + private + FIterator: IIterator; + FFilterFunc: TCollectionFilterFunc; + protected + function TrueFirst: ICollectable; override; + function TrueNext: ICollectable; override; + procedure TrueRemove; override; + public + constructor Create(const Iterator: IIterator; FilterFunc: TCollectionFilterFunc; AllowRemoval: Boolean = true); virtual; + destructor Destroy; override; + end; + + TKeyFilterMapIterator = class(TAbstractMapIterator) + private + FIterator: IMapIterator; + FFilter: IFilter; + protected + function TrueFirst: ICollectable; override; + function TrueNext: ICollectable; override; + procedure TrueRemove; override; + public + constructor Create(const Iterator: IMapIterator; const Filter: IFilter; AllowRemoval: Boolean = true); virtual; + destructor Destroy; override; + function CurrentKey: ICollectable; override; + end; + + TKeyFilterFuncMapIterator = class(TAbstractMapIterator) + private + FIterator: IMapIterator; + FFilterFunc: TCollectionFilterFunc; + protected + function TrueFirst: ICollectable; override; + function TrueNext: ICollectable; override; + procedure TrueRemove; override; + public + constructor Create(const Iterator: IMapIterator; FilterFunc: TCollectionFilterFunc; AllowRemoval: Boolean = true); virtual; + destructor Destroy; override; + function CurrentKey: ICollectable; override; + end; + + + ECollectionError = class(Exception) + private + FCollection: ICollection; + FErrorType: TCollectionError; + public + constructor Create(const Msg: String; const Collection: ICollection; ErrorType: TCollectionError); + property Collection: ICollection read FCollection; + property ErrorType: TCollectionError read FErrorType; + end; + +implementation + +uses + Math, + CollArray, CollHash, CollList, CollPArray, CollWrappers; + +var + FDefaultComparator: IComparator; + FNaturalComparator: IComparator; + FReverseNaturalComparator: IComparator; + +{ TCollectionPosition } +constructor TCollectionPosition.Create(Found: Boolean); +begin + FFound := Found; +end; + +{ TAbstractComparator } +class function TAbstractComparator.GetDefaultComparator: IComparator; +begin + if FDefaultComparator = nil then + FDefaultComparator := TDefaultComparator.Create; + Result := FDefaultComparator; +end; + +class function TAbstractComparator.GetNaturalComparator: IComparator; +begin + if FNaturalComparator = nil then + FNaturalComparator := TNaturalComparator.Create; + Result := FNaturalComparator; +end; + +class function TAbstractComparator.GetReverseNaturalComparator: IComparator; +begin + if FReverseNaturalComparator = nil then + FReverseNaturalComparator := TReverseNaturalComparator.Create; + Result := FReverseNaturalComparator; +end; + +function TAbstractComparator.GetInstance: TObject; +begin + Result := Self; +end; + +function TAbstractComparator.Equals(const Comparator: IComparator): Boolean; +begin + Result := (Self = Comparator.GetInstance); +end; + +{ TDefaultComparator } +constructor TDefaultComparator.Create; +begin + // Empty +end; + +function TDefaultComparator.Compare(const Item1, Item2: ICollectable): Integer; +var + Value1, Value2: Integer; +begin + if Item1 <> nil then + Value1 := Integer(Pointer(Item1)) + else + Value1 := Low(Integer); + if Item2 <> nil then + Value2 := Integer(Pointer(Item2)) + else + Value2 := Low(Integer); + if (Value1 < Value2) then + Result := -1 + else if (Value1 > Value2) then + Result := 1 + else + Result := 0; +end; + +function TDefaultComparator.Equals(const Item1, Item2: ICollectable): Boolean; +begin + Result := (Item1 = Item2); +end; + +{ TNaturalComparator } +constructor TNaturalComparator.Create; +begin + // Empty +end; + +function TNaturalComparator.Compare(const Item1, Item2: ICollectable): Integer; +begin + if (Item1 = nil) and (Item2 <> nil) then + Result := -1 + else if (Item1 <> nil) and (Item2 = nil) then + Result := 1 + else if (Item1 = nil) and (Item2 = nil) then + Result := 0 + else + Result := (Item1 as IComparable).CompareTo(Item2); +end; + +function TNaturalComparator.Equals(const Item1, Item2: ICollectable): Boolean; +begin + if (Item1 = nil) or (Item2 = nil) then + Result := (Item1 = Item2) + else + begin + Result := (Item1 as IEquatable).Equals(Item2); + end; +end; + +{ TReverseNaturalComparator } +constructor TReverseNaturalComparator.Create; +begin + // Empty +end; + +function TReverseNaturalComparator.Compare(const Item1, Item2: ICollectable): Integer; +begin + if (Item1 = nil) and (Item2 <> nil) then + Result := 1 + else if (Item1 <> nil) and (Item2 = nil) then + Result := -1 + else if (Item1 = nil) and (Item2 = nil) then + Result := 0 + else + Result := -(Item1 as IComparable).CompareTo(Item2); +end; + +function TReverseNaturalComparator.Equals(const Item1, Item2: ICollectable): Boolean; +begin + if (Item1 = nil) or (Item2 = nil) then + Result := (Item1 = Item2) + else + Result := (Item1 as IEquatable).Equals(Item2); +end; + +{ TAssociation } +constructor TAssociation.Create(const Key, Value: ICollectable); +begin + FKey := Key; + FValue := Value; +end; + +destructor TAssociation.Destroy; +begin + FKey := nil; + FValue := nil; + inherited Destroy; +end; + +function TAssociation.GetInstance: TObject; +begin + Result := Self; +end; + +function TAssociation.GetKey: ICollectable; +begin + Result := FKey; +end; + +function TAssociation.GetValue: ICollectable; +begin + Result := FValue; +end; + + +{ TIntegerAssociation } +constructor TIntegerAssociation.Create(const Key: Integer; const Value: ICollectable); +begin + FKey := Key; + FValue := Value; +end; + +destructor TIntegerAssociation.Destroy; +begin + FValue := nil; + inherited Destroy; +end; + +function TIntegerAssociation.GetInstance: TObject; +begin + Result := Self; +end; + +function TIntegerAssociation.GetKey: Integer; +begin + Result := FKey; +end; + +function TIntegerAssociation.GetValue: ICollectable; +begin + Result := FValue; +end; + + +{ TStringAssociation } +constructor TStringAssociation.Create(const Key: String; const Value: ICollectable); +begin + FKey := Key; + FValue := Value; +end; + +destructor TStringAssociation.Destroy; +begin + FValue := nil; + inherited Destroy; +end; + +function TStringAssociation.GetInstance: TObject; +begin + Result := Self; +end; + +function TStringAssociation.GetKey: String; +begin + Result := FKey; +end; + +function TStringAssociation.GetValue: ICollectable; +begin + Result := FValue; +end; + + +{ TAbstractIterator } +constructor TAbstractIterator.Create(AllowRemoval: Boolean); +begin + inherited Create; + FAllowRemoval := AllowRemoval; + FEOF := true; + FItem := nil; +end; + +procedure TAbstractIterator.AfterConstruction; +begin + inherited AfterConstruction; + First; +end; + +function TAbstractIterator.GetAllowRemoval: Boolean; +begin + Result := FAllowRemoval; +end; + +function TAbstractIterator.CurrentItem: ICollectable; +begin + Result := FItem; +end; + +function TAbstractIterator.EOF: Boolean; +begin + Result := FEOF; +end; + +function TAbstractIterator.First: ICollectable; +begin + FEOF := false; + FItem := TrueFirst; + if FItem = nil then + FEOF := true; + Result := FItem; +end; + +function TAbstractIterator.Next: ICollectable; +begin + if not FEOF then + begin + FItem := TrueNext; + if FItem = nil then + FEOF := true; + end; + Result := FItem; +end; + +function TAbstractIterator.Remove: Boolean; +begin + if (FItem <> nil) and FAllowRemoval then + begin + TrueRemove; + FItem := nil; + Result := true; + end + else + Result := false; +end; + +{ TAbstractAssociationIterator } +constructor TAbstractAssociationIterator.Create(AllowRemoval: Boolean); +begin + inherited Create; + FAllowRemoval := AllowRemoval; + FEOF := true; + FAssociation := nil; +end; + +procedure TAbstractAssociationIterator.AfterConstruction; +begin + inherited AfterConstruction; + First; +end; + +function TAbstractAssociationIterator.GetAllowRemoval: Boolean; +begin + Result := FAllowRemoval; +end; + +function TAbstractAssociationIterator.CurrentKey: ICollectable; +begin + if FAssociation <> nil then + Result := FAssociation.GetKey + else + Result := nil; +end; + +function TAbstractAssociationIterator.CurrentItem: ICollectable; +begin + if FAssociation <> nil then + Result := FAssociation.GetValue + else + Result := nil; +end; + +function TAbstractAssociationIterator.EOF: Boolean; +begin + Result := FEOF; +end; + +function TAbstractAssociationIterator.First: ICollectable; +begin + FAssociation := TrueFirst; + if FAssociation <> nil then + begin + Result := FAssociation.GetValue; + FEOF := false; + end + else + begin + Result := nil; + FEOF := true; + end; +end; + +function TAbstractAssociationIterator.Next: ICollectable; +begin + if not FEOF then + begin + FAssociation := TrueNext; + if FAssociation <> nil then + Result := FAssociation.GetValue + else + begin + Result := nil; + FEOF := true; + end; + end; +end; + +function TAbstractAssociationIterator.Remove: Boolean; +begin + if (FAssociation <> nil) and FAllowRemoval then + begin + TrueRemove; + FAssociation := nil; + Result := true; + end + else + Result := false; +end; + +{ TAbstractIntegerAssociationIterator } +constructor TAbstractIntegerAssociationIterator.Create(AllowRemoval: Boolean); +begin + inherited Create; + FAllowRemoval := AllowRemoval; + FEOF := true; + FAssociation := nil; +end; + +procedure TAbstractIntegerAssociationIterator.AfterConstruction; +begin + inherited AfterConstruction; + First; +end; + +function TAbstractIntegerAssociationIterator.GetAllowRemoval: Boolean; +begin + Result := FAllowRemoval; +end; + +function TAbstractIntegerAssociationIterator.CurrentKey: Integer; +begin + if FAssociation <> nil then + Result := FAssociation.GetKey + else + Result := 0; +end; + +function TAbstractIntegerAssociationIterator.CurrentItem: ICollectable; +begin + if FAssociation <> nil then + Result := FAssociation.GetValue + else + Result := nil; +end; + +function TAbstractIntegerAssociationIterator.EOF: Boolean; +begin + Result := FEOF; +end; + +function TAbstractIntegerAssociationIterator.First: ICollectable; +begin + FAssociation := TrueFirst; + if FAssociation <> nil then + begin + Result := FAssociation.GetValue; + FEOF := false; + end + else + begin + Result := nil; + FEOF := true; + end; +end; + +function TAbstractIntegerAssociationIterator.Next: ICollectable; +begin + if not FEOF then + begin + FAssociation := TrueNext; + if FAssociation <> nil then + Result := FAssociation.GetValue + else + begin + Result := nil; + FEOF := true; + end; + end; +end; + +function TAbstractIntegerAssociationIterator.Remove: Boolean; +begin + if (FAssociation <> nil) and FAllowRemoval then + begin + TrueRemove; + FAssociation := nil; + Result := true; + end + else + Result := false; +end; + +{ TAbstractStringAssociationIterator } +constructor TAbstractStringAssociationIterator.Create(AllowRemoval: Boolean); +begin + inherited Create; + FAllowRemoval := AllowRemoval; + FEOF := true; + FAssociation := nil; +end; + +procedure TAbstractStringAssociationIterator.AfterConstruction; +begin + inherited AfterConstruction; + First; +end; + +function TAbstractStringAssociationIterator.GetAllowRemoval: Boolean; +begin + Result := FAllowRemoval; +end; + +function TAbstractStringAssociationIterator.CurrentKey: String; +begin + if FAssociation <> nil then + Result := FAssociation.GetKey + else + Result := ''; +end; + +function TAbstractStringAssociationIterator.CurrentItem: ICollectable; +begin + if FAssociation <> nil then + Result := FAssociation.GetValue + else + Result := nil; +end; + +function TAbstractStringAssociationIterator.EOF: Boolean; +begin + Result := FEOF; +end; + +function TAbstractStringAssociationIterator.First: ICollectable; +begin + FAssociation := TrueFirst; + if FAssociation <> nil then + begin + Result := FAssociation.GetValue; + FEOF := false; + end + else + begin + Result := nil; + FEOF := true; + end; +end; + +function TAbstractStringAssociationIterator.Next: ICollectable; +begin + if not FEOF then + begin + FAssociation := TrueNext; + if FAssociation <> nil then + Result := FAssociation.GetValue + else + begin + Result := nil; + FEOF := true; + end; + end; +end; + +function TAbstractStringAssociationIterator.Remove: Boolean; +begin + if (FAssociation <> nil) and FAllowRemoval then + begin + TrueRemove; + FAssociation := nil; + Result := true; + end + else + Result := false; +end; + +{ TAssociationIterator } +constructor TAssociationIterator.Create(const Iterator: IIterator); +begin + inherited Create(Iterator.GetAllowRemoval); + FIterator := Iterator; +end; + +destructor TAssociationIterator.Destroy; +begin + FIterator := nil; + inherited Destroy; +end; + +function TAssociationIterator.TrueFirst: ICollectable; +var + Association: IAssociation; +begin + Association := FIterator.First as IAssociation; + if Association <> nil then + Result := Association.GetValue + else + Result := nil; +end; + +function TAssociationIterator.TrueNext: ICollectable; +var + Association: IAssociation; +begin + Association := (FIterator.Next as IAssociation); + if Association <> nil then + Result := Association.GetValue + else + Result := nil; +end; + +procedure TAssociationIterator.TrueRemove; +begin + FIterator.Remove; +end; + +function TAssociationIterator.CurrentItem: ICollectable; +var + Association: IAssociation; +begin + Association := FIterator.CurrentItem as IAssociation; + if Association <> nil then + Result := Association.GetValue + else + Result := nil; +end; + +function TAssociationIterator.CurrentKey: ICollectable; +var + Association: IAssociation; +begin + Association := FIterator.CurrentItem as IAssociation; + if Association <> nil then + Result := Association.GetKey + else + Result := nil; +end; + +{ TAssociationComparator } +constructor TAssociationComparator.Create(NaturalKeys: Boolean); +begin + inherited Create; + if NaturalKeys then + FKeyComparator := TAbstractComparator.GetNaturalComparator + else + FKeyComparator := TAbstractComparator.GetDefaultComparator; +end; + +destructor TAssociationComparator.Destroy; +begin + FKeyComparator := nil; + inherited Destroy; +end; + +function TAssociationComparator.GetKeyComparator: IComparator; +begin + Result := FKeyComparator; +end; + +procedure TAssociationComparator.SetKeyComparator(Value: IComparator); +begin + FKeyComparator := Value; +end; + +function TAssociationComparator.Compare(const Item1, Item2: ICollectable): Integer; +begin + Result := KeyComparator.Compare((Item1 as IAssociation).GetKey, (Item2 as IAssociation).GetKey); +end; + +function TAssociationComparator.Equals(const Item1, Item2: ICollectable): Boolean; +begin + Result := KeyComparator.Equals((Item1 as IAssociation).GetKey, (Item2 as IAssociation).GetKey); +end; + +{ TIntegerAssociationComparator } +constructor TIntegerAssociationComparator.Create; +begin + inherited Create; +end; + +destructor TIntegerAssociationComparator.Destroy; +begin + inherited Destroy; +end; + +function TIntegerAssociationComparator.Compare(const Item1, Item2: ICollectable): Integer; +var + Key1, Key2: Integer; +begin + Key1 := (Item1 as IIntegerAssociation).GetKey; + Key2 := (Item2 as IIntegerAssociation).GetKey; + if Key1 < Key2 then + Result := -1 + else if Key1 > Key2 then + Result := 1 + else + Result := 0; +end; + +function TIntegerAssociationComparator.Equals(const Item1, Item2: ICollectable): Boolean; +begin + Result := ((Item1 as IIntegerAssociation).GetKey = (Item2 as IIntegerAssociation).GetKey); +end; + +{ TStringAssociationComparator } +constructor TStringAssociationComparator.Create; +begin + inherited Create; +end; + +destructor TStringAssociationComparator.Destroy; +begin + inherited Destroy; +end; + +function TStringAssociationComparator.Compare(const Item1, Item2: ICollectable): Integer; +var + Key1, Key2: String; +begin + Key1 := (Item1 as IStringAssociation).GetKey; + Key2 := (Item2 as IStringAssociation).GetKey; + if Key1 < Key2 then + Result := -1 + else if Key1 > Key2 then + Result := 1 + else + Result := 0; +end; + +function TStringAssociationComparator.Equals(const Item1, Item2: ICollectable): Boolean; +begin + Result := ((Item1 as IStringAssociation).GetKey = (Item2 as IStringAssociation).GetKey); +end; + +{ TAssociationKeyIterator } +constructor TAssociationKeyIterator.Create(const Iterator: IMapIterator); +begin + inherited Create(Iterator.GetAllowRemoval); + FIterator := Iterator; +end; + +destructor TAssociationKeyIterator.Destroy; +begin + FIterator := nil; + inherited Destroy; +end; + +function TAssociationKeyIterator.TrueFirst: ICollectable; +begin + FIterator.First; + Result := FIterator.CurrentKey; +end; + +function TAssociationKeyIterator.TrueNext: ICollectable; +begin + FIterator.Next; + Result := FIterator.CurrentKey; +end; + +procedure TAssociationKeyIterator.TrueRemove; +begin + FIterator.Remove; +end; + +{ TFilterIterator } +constructor TFilterIterator.Create(const Iterator: IIterator; const Filter: IFilter; AllowRemoval: Boolean = true); +begin + FIterator := Iterator; + FFilter := Filter; +end; + +destructor TFilterIterator.Destroy; +begin + FIterator := nil; + FFilter := nil; +end; + +function TFilterIterator.TrueFirst: ICollectable; +var + Item: ICollectable; +begin + Item := FIterator.First; + while not FIterator.EOF do + begin + if FFilter.Accept(Item) then + break + else + Item := FIterator.Next; + end; + Result := Item; +end; + +function TFilterIterator.TrueNext: ICollectable; +var + Item: ICollectable; +begin + Item := FIterator.Next; + while not FIterator.EOF do + begin + if FFilter.Accept(Item) then + break + else + Item := FIterator.Next; + end; + Result := Item; +end; + +procedure TFilterIterator.TrueRemove; +begin + FIterator.Remove; +end; + +{ TFilterFuncIterator } +constructor TFilterFuncIterator.Create(const Iterator: IIterator; FilterFunc: TCollectionFilterFunc; AllowRemoval: Boolean = true); +begin + FIterator := Iterator; + FFilterFunc := FilterFunc; +end; + +destructor TFilterFuncIterator.Destroy; +begin + FIterator := nil; + FFilterFunc := nil; +end; + +function TFilterFuncIterator.TrueFirst: ICollectable; +var + Item: ICollectable; +begin + Item := FIterator.First; + while not FIterator.EOF do + begin + if FFilterFunc(Item) then + break + else + Item := FIterator.Next; + end; + Result := Item; +end; + +function TFilterFuncIterator.TrueNext: ICollectable; +var + Item: ICollectable; +begin + Item := FIterator.Next; + while not FIterator.EOF do + begin + if FFilterFunc(Item) then + break + else + Item := FIterator.Next; + end; + Result := Item; +end; + +procedure TFilterFuncIterator.TrueRemove; +begin + FIterator.Remove; +end; + +{ TKeyFilterMapIterator } +constructor TKeyFilterMapIterator.Create(const Iterator: IMapIterator; const Filter: IFilter; AllowRemoval: Boolean = true); +begin + FIterator := Iterator; + FFilter := Filter; +end; + +destructor TKeyFilterMapIterator.Destroy; +begin + FIterator := nil; + FFilter := nil; +end; + +function TKeyFilterMapIterator.TrueFirst: ICollectable; +var + Key, Item: ICollectable; +begin + Item := FIterator.First; + while not FIterator.EOF do + begin + Key := FIterator.CurrentKey; + if FFilter.Accept(Key) then + break + else + Item := FIterator.Next; + end; + Result := Item; +end; + +function TKeyFilterMapIterator.TrueNext: ICollectable; +var + Key, Item: ICollectable; +begin + Item := FIterator.Next; + while not FIterator.EOF do + begin + Key := FIterator.CurrentKey; + if FFilter.Accept(Key) then + break + else + Item := FIterator.Next; + end; + Result := Item; +end; + +procedure TKeyFilterMapIterator.TrueRemove; +begin + FIterator.Remove; +end; + +function TKeyFilterMapIterator.CurrentKey: ICollectable; +begin + Result := FIterator.CurrentKey; +end; + +{ TKeyFilterFuncMapIterator } +constructor TKeyFilterFuncMapIterator.Create(const Iterator: IMapIterator; FilterFunc: TCollectionFilterFunc; AllowRemoval: Boolean = true); +begin + FIterator := Iterator; + FFilterFunc := FilterFunc; +end; + +destructor TKeyFilterFuncMapIterator.Destroy; +begin + FIterator := nil; + FFilterFunc := nil; +end; + +function TKeyFilterFuncMapIterator.TrueFirst: ICollectable; +var + Key, Item: ICollectable; +begin + Item := FIterator.First; + while not FIterator.EOF do + begin + Key := FIterator.CurrentKey; + if FFilterFunc(Key) then + break + else + Item := FIterator.Next; + end; + Result := Item; +end; + +function TKeyFilterFuncMapIterator.TrueNext: ICollectable; +var + Key, Item: ICollectable; +begin + Item := FIterator.Next; + while not FIterator.EOF do + begin + Key := FIterator.CurrentKey; + if FFilterFunc(Key) then + break + else + Item := FIterator.Next; + end; + Result := Item; +end; + +procedure TKeyFilterFuncMapIterator.TrueRemove; +begin + FIterator.Remove; +end; + +function TKeyFilterFuncMapIterator.CurrentKey: ICollectable; +begin + Result := FIterator.CurrentKey; +end; + + +{ TAbstractCollection } +constructor TAbstractCollection.Create; +begin + Create(false); +end; + +constructor TAbstractCollection.Create(NaturalItemsOnly: Boolean); +begin + FCreated := false; + inherited Create; + FNaturalItemsOnly := NaturalItemsOnly or GetAlwaysNaturalItems; + if FNaturalItemsOnly then + FComparator := TAbstractComparator.GetNaturalComparator + else + FComparator := TAbstractComparator.GetDefaultComparator; + FIgnoreErrors := [ceDuplicate]; +end; + +constructor TAbstractCollection.Create(const ItemArray: array of ICollectable); +begin + Create(ItemArray, false); +end; + +// Fixed size collections must override this. +constructor TAbstractCollection.Create(const ItemArray: array of ICollectable; NaturalItemsOnly: Boolean); +var + I: Integer; +begin + Create(NaturalItemsOnly); + if not FixedSize then + begin + Capacity := Length(ItemArray); + for I := Low(ItemArray) to High(ItemArray) do + begin + Add(ItemArray[I]); + end; + end; +end; + +// Fixed size collections must override this. +constructor TAbstractCollection.Create(const Collection: ICollection); +var + Iterator: IIterator; +begin + Create(Collection.GetNaturalItemsOnly); + InitFrom(Collection); + if not FixedSize then + begin + Capacity := Collection.GetSize; + Iterator := Collection.GetIterator; + while not Iterator.EOF do + begin + Add(Iterator.CurrentItem); + Iterator.Next; + end; + end; +end; + +destructor TAbstractCollection.Destroy; +begin + FCreated := false; + FComparator := nil; + inherited Destroy; +end; + +procedure TAbstractCollection.CollectionError(ErrorType: TCollectionError); +var + Msg: String; +begin + if not (ErrorType in FIgnoreErrors) then + begin + case ErrorType of + ceDuplicate: Msg := 'Collection does not allow duplicates.'; + ceDuplicateKey: Msg := 'Collection does not allow duplicate keys.'; + ceFixedSize: Msg := 'Collection has fixed size.'; + ceNilNotAllowed: Msg := 'Collection does not allow nil.'; + ceNotNaturalItem: Msg := 'Collection only accepts natural items.'; + ceOutOfRange: Msg := 'Index out of collection range.'; + end; + // If exception is thrown during construction, collection cannot be + // passed to it as destructor is automatically called and this leaves an + // interface reference to a destroyed object and crashes. + if FCreated then + raise ECollectionError.Create(Msg, Self, ErrorType) + else + raise ECollectionError.Create(Msg, nil, ErrorType); + end; +end; + +procedure TAbstractCollection.InitFrom(const Collection: ICollection); +begin + Comparator := Collection.GetComparator; + IgnoreErrors := Collection.GetIgnoreErrors; +end; + +// Implementations should override this if possible +function TAbstractCollection.TrueItemCount(const Item: ICollectable): Integer; +var + Iterator: IIterator; + Total: Integer; +begin + Total := 0; + Iterator := GetIterator; + while not Iterator.EOF do + begin + if FComparator.Equals(Item, Iterator.CurrentItem) then + Inc(Total); + Iterator.Next; + end; + Result := Total; +end; + +class function TAbstractCollection.GetAlwaysNaturalItems: Boolean; +begin + Result := false; +end; + +function TAbstractCollection.GetAsArray: TCollectableArray; +var + Iterator: IIterator; + Working: TCollectableArray; + I: Integer; +begin + SetLength(Working, Size); + I := 0; + Iterator := GetIterator; + while not Iterator.EOF do + begin + Working[I] := Iterator.CurrentItem; + Inc(I); + Iterator.Next; + end; + Result := Working; +end; + +function TAbstractCollection.GetComparator: IComparator; +begin + Result := FComparator; +end; + +function TAbstractCollection.GetDuplicates: Boolean; +begin + Result := true; // Sets and lists override this. +end; + +procedure TAbstractCollection.SetComparator(const Value: IComparator); +begin + if Value = nil then + begin + if NaturalItemsOnly then + FComparator := TAbstractComparator.GetNaturalComparator + else + FComparator := TAbstractComparator.GetDefaultComparator; + end + else + FComparator := Value; +end; + +function TAbstractCollection.GetFixedSize: Boolean; +begin + Result := false; +end; + +function TAbstractCollection.GetIgnoreErrors: TCollectionErrors; +begin + Result := FIgnoreErrors; +end; + +procedure TAbstractCollection.SetIgnoreErrors(Value: TCollectionErrors); +begin + FIgnoreErrors := Value; +end; + +function TAbstractCollection.GetInstance: TObject; +begin + Result := Self; +end; + +function TAbstractCollection.GetIterator(const Filter: IFilter): IIterator; +var + Iterator: IIterator; +begin + Iterator := GetIterator; + Result := TFilterIterator.Create(Iterator, Filter, Iterator.GetAllowRemoval); +end; + +function TAbstractCollection.GetIterator(FilterFunc: TCollectionFilterFunc): IIterator; +var + Iterator: IIterator; +begin + Iterator := GetIterator; + Result := TFilterFuncIterator.Create(Iterator, FilterFunc, Iterator.GetAllowRemoval); +end; + +function TAbstractCollection.GetNaturalItemsOnly: Boolean; +begin + Result := FNaturalItemsOnly; +end; + +function TAbstractCollection.Add(const Item: ICollectable): Boolean; +var + ItemError: TCollectionError; + Success: Boolean; +begin + ItemError := ItemAllowed(Item); // Can be natural items only error or nil not allowed error + if ItemError <> ceOK then + begin + CollectionError(ItemError); + Success := false; + end + else if FixedSize then + begin + CollectionError(ceFixedSize); + Success := false; + end + else + begin + Success := TrueAdd(Item); + end; + Result := Success; +end; + +function TAbstractCollection.Add(const ItemArray: array of ICollectable): Integer; +var + Item: ICollectable; + ItemError: TCollectionError; + I, Count: Integer; + Success: Boolean; +begin + Count := 0; + if FixedSize then + begin + CollectionError(ceFixedSize); + end + else + begin + for I := Low(ItemArray) to High(ItemArray) do + begin + Item := ItemArray[I]; + ItemError := ItemAllowed(Item); + if ItemError <> ceOK then + begin + CollectionError(ItemError); + Success := false; + end + else + begin + Success := TrueAdd(Item); + end; + if Success then + Inc(Count); + end; + end; + Result := Count; +end; + +function TAbstractCollection.Add(const Collection: ICollection): Integer; +var + Iterator: IIterator; + Item: ICollectable; + ItemError: TCollectionError; + Count: Integer; + Success: Boolean; +begin + Count := 0; + Iterator := Collection.GetIterator; + while not Iterator.EOF do + begin + Item := Iterator.CurrentItem; + ItemError := ItemAllowed(Item); + if ItemError <> ceOK then + begin + CollectionError(ItemError); + Success := false; + end + else if FixedSize then + begin + CollectionError(ceFixedSize); + Success := false; + end + else + begin + Success := TrueAdd(Item); + end; + if Success then + Inc(Count); + Iterator.Next; + end; + Result := Count; +end; + +procedure TAbstractCollection.AfterConstruction; +begin + inherited AfterConstruction; + FCreated := true; +end; + +procedure TAbstractCollection.BeforeDestruction; +begin + if not FixedSize then + TrueClear; + inherited BeforeDestruction; +end; + +function TAbstractCollection.Clear: Integer; +begin + if not FixedSize then + begin + Result := Size; + TrueClear; + end + else + begin + CollectionError(ceFixedSize); + Result := 0; + end; +end; + +function TAbstractCollection.Clone: ICollection; +begin + Result := (TAbstractCollectionClass(ClassType)).Create(Self); +end; + +function TAbstractCollection.Contains(const Item: ICollectable): Boolean; +var + ItemError: TCollectionError; + Success: Boolean; +begin + ItemError := ItemAllowed(Item); + if ItemError <> ceOK then + begin + CollectionError(ItemError); + Success := false; + end + else + begin + Success := TrueContains(Item); + end; + Result := Success; +end; + +function TAbstractCollection.Contains(const ItemArray: array of ICollectable): Boolean; +var + I: Integer; + Success: Boolean; +begin + Success := true; + for I := Low(ItemArray) to High(ItemArray) do + begin + Success := Success and Contains(ItemArray[I]); + if not Success then + break; + end; + Result := Success; +end; + +function TAbstractCollection.Contains(const Collection: ICollection): Boolean; +var + Iterator: IIterator; + Success: Boolean; +begin + Success := true; + Iterator := Collection.GetIterator; + while not Iterator.EOF do + begin + Success := Success and Contains(Iterator.CurrentItem); + if not Success then + break; + Iterator.Next; + end; + Result := Success; +end; + +function TAbstractCollection.Equals(const Collection: ICollection): Boolean; +var + Iterator: IIterator; + Success: Boolean; +begin + if Collection.GetType <> GetType then + Result := false + else if Collection.Size <> Size then + Result := false + else if not Collection.Comparator.Equals(Comparator) then + Result := false + else if not Collection.GetDuplicates and not GetDuplicates then + begin + // Not equal if any item not found in parameter collection + Success := true; + Iterator := GetIterator; + while not Iterator.EOF and Success do + begin + Success := Collection.Contains(Iterator.CurrentItem); + Iterator.Next; + end; + Result := Success; + end + else + begin + // Not equal if any item count not equal to item count in parameter collection + Success := true; + Iterator := GetIterator; + while not Iterator.EOF and Success do + begin + Success := (ItemCount(Iterator.CurrentItem) = Collection.ItemCount(Iterator.CurrentItem)); + Iterator.Next; + end; + Result := Success; + end; +end; + +function TAbstractCollection.Find(const Filter: IFilter): ICollectable; +begin + Result := GetIterator(Filter).First; +end; + +function TAbstractCollection.Find(FilterFunc: TCollectionFilterFunc): ICollectable; +begin + Result := GetIterator(FilterFunc).First; +end; + +function TAbstractCollection.FindAll(const Filter: IFilter): ICollection; +var + ResultCollection: ICollection; + Iterator: IIterator; +begin + ResultCollection := TPArrayBag.Create(NaturalItemsOnly); + Iterator := Self.GetIterator(Filter); + while not Iterator.EOF do + begin + ResultCollection.Add(Iterator.CurrentItem); + Iterator.Next; + end; + Result := ResultCollection; +end; + +function TAbstractCollection.FindAll(FilterFunc: TCollectionFilterFunc): ICollection; +var + ResultCollection: ICollection; + Iterator: IIterator; +begin + ResultCollection := TPArrayBag.Create(NaturalItemsOnly); + Iterator := Self.GetIterator(FilterFunc); + while not Iterator.EOF do + begin + ResultCollection.Add(Iterator.CurrentItem); + Iterator.Next; + end; + Result := ResultCollection; +end; + +function TAbstractCollection.IsEmpty: Boolean; +begin + Result := (Size = 0); +end; + +function TAbstractCollection.IsNaturalItem(const Item: ICollectable): Boolean; +var + Temp: IUnknown; +begin + if Item <> nil then + Result := (Item.QueryInterface(NaturalItemIID, Temp) <> E_NOINTERFACE) + else + Result := false; +end; + +function TAbstractCollection.ItemAllowed(const Item: ICollectable): TCollectionError; +begin + if NaturalItemsOnly and not IsNaturalItem(Item) then + Result := ceNotNaturalItem + else if not IsNilAllowed and (Item = nil) then + Result := ceNilNotAllowed + else + Result := ceOK; +end; + +function TAbstractCollection.ItemCount(const Item: ICollectable): Integer; +var + ItemError: TCollectionError; +begin + ItemError := ItemAllowed(Item); + if ItemError <> ceOK then + begin + CollectionError(ItemError); + Result := 0; + end + else if GetDuplicates then + begin + Result := TrueItemCount(Item); + end + else + begin + // Where duplicates are not allowed, TrueContains will be faster than TrueItemCount. + if TrueContains(Item) then + Result := 1 + else + Result := 0; + end; +end; + +function TAbstractCollection.ItemCount(const ItemArray: array of ICollectable): Integer; +var + I: Integer; + Total: Integer; +begin + Total := 0; + for I := Low(ItemArray) to High(ItemArray) do + begin + Total := Total + ItemCount(ItemArray[I]); + end; + Result := Total; +end; + +function TAbstractCollection.ItemCount(const Collection: ICollection): Integer; +var + Iterator: IIterator; + Total: Integer; +begin + Total := 0; + Iterator := Collection.GetIterator; + while not Iterator.EOF do + begin + Total := Total + ItemCount(Iterator.CurrentItem); + Iterator.Next; + end; + Result := Total; +end; + +function TAbstractCollection.Matching(const ItemArray: array of ICollectable): ICollection; +var + ResultCollection: ICollection; + I: Integer; +begin + ResultCollection := TPArrayBag.Create(NaturalItemsOnly); + for I := Low(ItemArray) to High(ItemArray) do + begin + if Contains(ItemArray[I]) then + ResultCollection.Add(ItemArray[I]); + end; + Result := ResultCollection; +end; + +function TAbstractCollection.Matching(const Collection: ICollection): ICollection; +var + ResultCollection: ICollection; + Iterator: IIterator; +begin + ResultCollection := TPArrayBag.Create(NaturalItemsOnly); + Iterator := Collection.GetIterator; + while not Iterator.EOF do + begin + if Contains(Iterator.CurrentItem) then + ResultCollection.Add(Iterator.CurrentItem); + Iterator.Next; + end; + Result := ResultCollection; +end; + +function TAbstractCollection.Remove(const Item: ICollectable): ICollectable; +var + ItemError: TCollectionError; +begin + ItemError := ItemAllowed(Item); + if ItemError <> ceOK then + begin + CollectionError(ItemError); + Result := nil; + end + else if FixedSize then + begin + CollectionError(ceFixedSize); + Result := nil; + end + else + begin + Result := TrueRemove(Item); + end; +end; + +function TAbstractCollection.Remove(const ItemArray: array of ICollectable): ICollection; +var + ResultCollection: ICollection; + I: Integer; +begin + ResultCollection := TPArrayBag.Create(NaturalItemsOnly); + for I := Low(ItemArray) to High(ItemArray) do + begin + ResultCollection.Add(Remove(ItemArray[I])); + end; + Result := ResultCollection; +end; + +function TAbstractCollection.Remove(const Collection: ICollection): ICollection; +var + ResultCollection: ICollection; + Iterator: IIterator; +begin + ResultCollection := TPArrayBag.Create(NaturalItemsOnly); + Iterator := Collection.GetIterator; + while not Iterator.EOF do + begin + ResultCollection.Add(Remove(Iterator.CurrentItem)); + Iterator.Next; + end; + Result := ResultCollection; +end; + +function TAbstractCollection.RemoveAll(const Item: ICollectable): ICollection; +var + ItemError: TCollectionError; +begin + ItemError := ItemAllowed(Item); + if ItemError <> ceOK then + begin + CollectionError(ItemError); + Result := nil; + end + else if FixedSize then + begin + CollectionError(ceFixedSize); + Result := nil; + end + else + begin + Result := TrueRemoveAll(Item); + end; +end; + +function TAbstractCollection.RemoveAll(const ItemArray: array of ICollectable): ICollection; +var + ResultCollection: ICollection; + I: Integer; +begin + ResultCollection := TPArrayBag.Create(NaturalItemsOnly); + for I := Low(ItemArray) to High(ItemArray) do + begin + ResultCollection.Add(RemoveAll(ItemArray[I])); + end; + Result := ResultCollection; +end; + +function TAbstractCollection.RemoveAll(const Collection: ICollection): ICollection; +var + ResultCollection: ICollection; + Iterator: IIterator; +begin + ResultCollection := TPArrayBag.Create(NaturalItemsOnly); + Iterator := Collection.GetIterator; + while not Iterator.EOF do + begin + ResultCollection.Add(RemoveAll(Iterator.CurrentItem)); + Iterator.Next; + end; + Result := ResultCollection; +end; + +function TAbstractCollection.Retain(const ItemArray: array of ICollectable): ICollection; +var + ResultCollection: ICollection; + Iterator: IIterator; + Item: ICollectable; + I: Integer; + Found, Success: Boolean; +begin + ResultCollection := TPArrayBag.Create(NaturalItemsOnly); + Iterator := GetIterator; + while not Iterator.EOF do + begin + // Converting the array to a map would be faster but I don't want to + // couple base class code to a complex collection. + Found := false; + for I := Low(ItemArray) to High(ItemArray) do + begin + Item := Iterator.CurrentItem; + Found := Comparator.Equals(Item, ItemArray[I]); + if Found then + break; + end; + if not Found then + begin + Success := Iterator.Remove; + if Success then + ResultCollection.Add(Item); + end; + Iterator.Next; + end; + Result := ResultCollection; +end; + +function TAbstractCollection.Retain(const Collection: ICollection): ICollection; +var + ResultCollection: ICollection; + Iterator: IIterator; + Item: ICollectable; + Success: Boolean; +begin + ResultCollection := TPArrayBag.Create(NaturalItemsOnly); + Iterator := GetIterator; + while not Iterator.EOF do + begin + Item := Iterator.CurrentItem; + if not Collection.Contains(Item) then + begin + Success := Iterator.Remove; + if Success then + ResultCollection.Add(Item); + end; + Iterator.Next; + end; + Result := ResultCollection; +end; + +{ TAbstractBag } +function TAbstractBag.CloneAsBag: IBag; +begin + Result := (TAbstractBagClass(ClassType)).Create(Self); +end; + +function TAbstractBag.GetNaturalItemIID: TGUID; +begin + Result := EquatableIID; +end; + +function TAbstractBag.GetType: TCollectionType; +begin + Result := ctBag; +end; + +function TAbstractBag.IsNilAllowed: Boolean; +begin + Result := true; +end; + +{ TAbstractSet } +function TAbstractSet.TrueAdd(const Item: ICollectable): Boolean; +var + Position: TCollectionPosition; +begin + // Adds if not already present otherwise fails + Position := GetPosition(Item); + try + if Position.Found then + begin + CollectionError(ceDuplicate); + Result := false; + end + else + begin + TrueAdd2(Position, Item); + Result := true; + end; + finally + Position.Free; + end; +end; + +function TAbstractSet.TrueContains(const Item: ICollectable): Boolean; +var + Position: TCollectionPosition; +begin + Position := GetPosition(Item); + try + Result := Position.Found; + finally + Position.Free; + end; +end; + +function TAbstractSet.TrueRemove(const Item: ICollectable): ICollectable; +var + Position: TCollectionPosition; +begin + Position := GetPosition(Item); + try + if Position.Found then + begin + Result := TrueGet(Position); + TrueRemove2(Position); + end + else + Result := nil; + finally + Position.Free; + end; +end; + +function TAbstractSet.TrueRemoveAll(const Item: ICollectable): ICollection; +var + ResultCollection: ICollection; + RemovedItem: ICollectable; +begin + ResultCollection := TPArrayBag.Create; + RemovedItem := TrueRemove(Item); + if RemovedItem <> nil then + ResultCollection.Add(RemovedItem); + Result := ResultCollection; +end; + +function TAbstractSet.GetDuplicates: Boolean; +begin + Result := false; +end; + +function TAbstractSet.GetNaturalItemIID: TGUID; +begin + Result := EquatableIID; +end; + +function TAbstractSet.GetType: TCollectionType; +begin + Result := ctSet; +end; + +function TAbstractSet.CloneAsSet: ISet; +begin + Result := (TAbstractSetClass(ClassType)).Create(Self); +end; + +function TAbstractSet.Complement(const Universe: ISet): ISet; +var + ResultSet: ISet; + Iterator: IIterator; + Item: ICollectable; +begin + // Return items in universe not found in self. + ResultSet := TAbstractSetClass(ClassType).Create(NaturalItemsOnly); + Iterator := Universe.GetIterator; + while not Iterator.EOF do + begin + Item := Iterator.CurrentItem; + if not Contains(Item) then + ResultSet.Add(Item); + Iterator.Next; + end; + Result := ResultSet; +end; + +function TAbstractSet.Intersect(const Set2: ISet): ISet; +var + ResultSet: ISet; + Iterator: IIterator; + Item: ICollectable; +begin + // Return items found in self and parameter. + ResultSet := TAbstractSetClass(ClassType).Create(NaturalItemsOnly); + Iterator := GetIterator; + while not Iterator.EOF do + begin + Item := Iterator.CurrentItem; + if Contains(Item) and Set2.Contains(Item) then + ResultSet.Add(Iterator.CurrentItem); + Iterator.Next; + end; + Result := ResultSet; +end; + +function TAbstractSet.IsNilAllowed: Boolean; +begin + Result := false; +end; + +function TAbstractSet.Union(const Set2: ISet): ISet; +var + ResultSet: ISet; + Iterator: IIterator; + Item: ICollectable; +begin + // Return items found in self or parameter. + ResultSet := CloneAsSet; + Iterator := Set2.GetIterator; + while not Iterator.EOF do + begin + Item := Iterator.CurrentItem; + if not Contains(Item) and Set2.Contains(Item) then + ResultSet.Add(Iterator.CurrentItem); + Iterator.Next; + end; + Result := ResultSet; +end; + +{ TAbstractList } +constructor TAbstractList.Create(NaturalItemsOnly: Boolean); +begin + inherited Create(NaturalItemsOnly); + FDuplicates := true; + FSorted := false; +end; + +procedure TAbstractList.InitFrom(const Collection: ICollection); +var + List: IList; +begin + inherited InitFrom(Collection); + if Collection.QueryInterface(IList, List) = S_OK then + begin + FDuplicates := List.GetDuplicates; + FSorted := List.GetSorted; + end; +end; + +function TAbstractList.TrueAdd(const Item: ICollectable): Boolean; +var + SearchResult: TSearchResult; +begin + Result := True; + if Sorted then + begin + // Insert in appropriate place to maintain sort order, unless duplicate + // not allowed. + SearchResult := BinarySearch(Item); + case SearchResult.ResultType of + srBeforeIndex: TrueInsert(SearchResult.Index, Item); + srFoundAtIndex: begin + if Duplicates then + TrueInsert(SearchResult.Index, Item) + else + begin + CollectionError(ceDuplicate); + Result := false; + end; + end; + srAfterEnd: TrueAppend(Item); + end; + end + else + begin + // Add to end, unless duplicate not allowed. + if not Duplicates and (SequentialSearch(Item, Comparator).ResultType = srFoundAtIndex) then + begin + CollectionError(ceDuplicate); + Result := false; + end + else + TrueAppend(Item); + end; +end; + +function TAbstractList.TrueContains(const Item: ICollectable): Boolean; +begin + if Sorted then + Result := BinarySearch(Item).ResultType = srFoundAtIndex + else + Result := SequentialSearch(Item, Comparator).ResultType = srFoundAtIndex +end; + +function TAbstractList.TrueItemCount(const Item: ICollectable): Integer; +var + SearchResult: TSearchResult; + Count: Integer; +begin + if Sorted then + begin + // If sorted, use binary search. + Count := 0; + SearchResult := BinarySearch(Item); + if SearchResult.ResultType = srFoundAtIndex then + begin + repeat + Inc(Count); + until not Comparator.Equals(Item, Items[SearchResult.Index]); + end; + Result := Count; + end + else + // Resort to sequential search for unsorted + Result := inherited TrueItemCount(Item); +end; + +function TAbstractList.TrueRemove(const Item: ICollectable): ICollectable; +var + SearchResult: TSearchResult; +begin + Result := nil; + if Sorted then + begin + SearchResult := BinarySearch(Item); + if SearchResult.ResultType = srFoundAtIndex then + begin + Result := TrueDelete(SearchResult.Index); + end; + end + else + begin + SearchResult := SequentialSearch(Item); + if SearchResult.ResultType = srFoundAtIndex then + Result := TrueDelete(SearchResult.Index); + end; +end; + +function TAbstractList.TrueRemoveAll(const Item: ICollectable): ICollection; +var + ResultCollection: ICollection; + SearchResult: TSearchResult; + I: Integer; +begin + ResultCollection := TPArrayBag.Create; + if Sorted then + begin + SearchResult := BinarySearch(Item); + if SearchResult.ResultType = srFoundAtIndex then + begin + repeat + ResultCollection.Add(TrueDelete(SearchResult.Index)); + until not Comparator.Equals(Item, Items[SearchResult.Index]); + end; + end + else + begin + I := 0; + while I < Size do + begin + if Comparator.Equals(Item, Items[I]) then + begin + ResultCollection.Add(TrueDelete(I)); + end + else + Inc(I); + end; + end; + Result := ResultCollection; +end; + +procedure TAbstractList.QuickSort(Lo, Hi: Integer; const Comparator: IComparator); +var + I, J, Mid: Integer; +begin + repeat + I := Lo; + J := Hi; + Mid := (Lo + Hi) div 2; + repeat + while Comparator.Compare(Items[I], Items[Mid]) < 0 do + Inc(I); + while Comparator.Compare(Items[J], Items[Mid]) > 0 do + Dec(J); + if I <= J then + begin + Exchange(I, J); + if Mid = I then + Mid := J + else if Mid = J then + Mid := I; + Inc(I); + Dec(J); + end; + until I > J; + if Lo < J then + QuickSort(Lo, J, Comparator); + Lo := I; + until I >= Hi; +end; + +procedure TAbstractList.QuickSort(Lo, Hi: Integer; CompareFunc: TCollectionCompareFunc); +var + I, J, Mid: Integer; +begin + repeat + I := Lo; + J := Hi; + Mid := (Lo + Hi) div 2; + repeat + while CompareFunc(Items[I], Items[Mid]) < 0 do + Inc(I); + while CompareFunc(Items[J], Items[Mid]) > 0 do + Dec(J); + if I <= J then + begin + Exchange(I, J); + if Mid = I then + Mid := J + else if Mid = J then + Mid := I; + Inc(I); + Dec(J); + end; + until I > J; + if Lo < J then + QuickSort(Lo, J, CompareFunc); + Lo := I; + until I >= Hi; +end; + +function TAbstractList.GetDuplicates: Boolean; +begin + Result := FDuplicates; +end; + +procedure TAbstractList.SetDuplicates(Value: Boolean); +var + Iterator: IIterator; + Failed: Boolean; +begin + Failed := false; + // If trying to set no duplicates, check there are no existing duplicates. + if not Value then + begin + Iterator := GetIterator; + while not Iterator.EOF and not Failed do + begin + Failed := (ItemCount(Iterator.CurrentItem) > 1); + Iterator.Next; + end; + if Failed then + CollectionError(ceDuplicate); + end; + if not Failed then + FDuplicates := Value; +end; + +function TAbstractList.GetItem(Index: Integer): ICollectable; +begin + if (Index < 0) or (Index >= Size) then + begin + CollectionError(ceOutOfRange); + Result := nil; + end + else + Result := TrueGetItem(Index); +end; + +procedure TAbstractList.SetItem(Index: Integer; const Item: ICollectable); +var + SearchResult: TSearchResult; +begin + if (Index < 0) or (Index >= Size) then + begin + CollectionError(ceOutOfRange) + end + else if not Duplicates then + begin + // Find any duplicates + if Sorted then + begin + SearchResult := BinarySearch(Item); + case SearchResult.ResultType of + srBeforeIndex, srAfterEnd: begin // If item is not present + FSorted := false; + TrueSetItem(Index, Item); + end; + srFoundAtIndex: begin // If item is already present + CollectionError(ceDuplicate); + end; + end; + end + else + begin + // If item is already present + if SequentialSearch(Item, Comparator).ResultType = srFoundAtIndex then + begin + CollectionError(ceDuplicate); + end + else + begin + TrueSetItem(Index, Item); + end; + end; + end + else + begin + FSorted := false; + TrueSetItem(Index, Item); + end; +end; + +function TAbstractList.GetIterator: IIterator; +begin + Result := TAbstractListIterator.Create(Self); +end; + +function TAbstractList.GetNaturalItemIID: TGUID; +begin + Result := ComparableIID; +end; + +function TAbstractList.GetSorted: Boolean; +begin + Result := FSorted; +end; + +procedure TAbstractList.SetSorted(Value: Boolean); +begin + if Value then + Sort; +end; + +function TAbstractList.GetType: TCollectionType; +begin + Result := ctList; +end; + +function TAbstractList.BinarySearch(const Item: ICollectable): TSearchResult; +var + Lo, Hi, Mid: Integer; + CompareResult: Integer; + Success: Boolean; +begin + if Size = 0 then + begin + Result.ResultType := srAfterEnd; + Exit; + end; + Lo := 0; + Hi := Size - 1; + Success := false; + repeat + Mid := (Lo + Hi) div 2; + CompareResult := Comparator.Compare(Item, Items[Mid]); + if CompareResult = 0 then + Success := true + else if CompareResult > 0 then + Lo := Mid + 1 + else + Hi := Mid - 1; + until (Lo > Hi) or Success; + if Success then + begin + // Move index back if in cluster of duplicates + while (Mid > 0) and Comparator.Equals(Item, Items[Mid - 1]) do + Dec(Mid); + Result.ResultType := srFoundAtIndex; + Result.Index := Mid; + end + else if CompareResult < 0 then + begin + Result.ResultType := srBeforeIndex; + Result.Index := Mid; + end + else if Hi < Size - 1 then + begin + Result.ResultType := srBeforeIndex; + Result.Index := Mid + 1; + end + else + Result.ResultType := srAfterEnd; +end; + +function TAbstractList.CloneAsList: IList; +begin + Result := (TAbstractListClass(ClassType)).Create(Self); +end; + +function TAbstractList.Delete(Index: Integer): ICollectable; +begin + if FixedSize then + begin + CollectionError(ceFixedSize); + Result := nil; + end + else if (Index < 0) or (Index >= Size) then + begin + CollectionError(ceOutOfRange); + Result := nil; + end + else + begin + Result := TrueDelete(Index); + end; +end; + +procedure TAbstractList.Exchange(Index1, Index2: Integer); +var + Item: ICollectable; +begin + if (Index1 < 0) or (Index1 >= Size) then + CollectionError(ceOutOfRange); + if (Index2 < 0) or (Index2 >= Size) then + CollectionError(ceOutOfRange); + FSorted := false; + Item := ICollectable(Items[Index1]); + Items[Index1] := Items[Index2]; + Items[Index2] := Item; +end; + +function TAbstractList.First: ICollectable; +begin + if Size > 0 then + Result := Items[0] + else + Result := nil; +end; + +function TAbstractList.IndexOf(const Item: ICollectable): Integer; +var + SearchResult: TSearchResult; +begin + if Sorted then + SearchResult := BinarySearch(Item) + else + SearchResult := SequentialSearch(Item, Comparator); + if SearchResult.ResultType = srFoundAtIndex then + Result := SearchResult.Index + else + Result := -1; +end; + +function TAbstractList.Insert(Index: Integer; const Item: ICollectable): Boolean; +var + ItemError: TCollectionError; +begin + ItemError := ItemAllowed(Item); + if ItemError <> ceOK then + begin + CollectionError(ItemError); + Result := false; + end + else if FixedSize then + begin + CollectionError(ceFixedSize); + Result := false; + end + else if (Index < 0) or (Index > Size) then + begin + CollectionError(ceOutOfRange); + Result := false; + end + else + begin + FSorted := false; + if Index = Size then + TrueAdd(Item) + else + TrueInsert(Index, Item); + Result := true; + end; +end; + +function TAbstractList.Insert(Index: Integer; const ItemArray: array of ICollectable): Integer; +var + Item: ICollectable; + ItemError: TCollectionError; + I, NewIndex, Count: Integer; + Success: Boolean; +begin + Count := 0; + if FixedSize then + begin + CollectionError(ceFixedSize); + end + else if (Index < 0) or (Index > Size) then + begin + CollectionError(ceOutOfRange); + end + else + begin + // Insert entire array in place in correct order + NewIndex := Index; + for I := Low(ItemArray) to High(ItemArray) do + begin + Item := ItemArray[I]; + ItemError := ItemAllowed(Item); + if ItemError <> ceOK then + begin + CollectionError(ItemError); + end + else + begin + Success := Insert(NewIndex, Item); + if Success then + begin + Inc(NewIndex); + Inc(Count); + end; + end; + end; + end; + Result := Count; +end; + +function TAbstractList.Insert(Index: Integer; const Collection: ICollection): Integer; +var + Iterator: IIterator; + Item: ICollectable; + ItemError: TCollectionError; + NewIndex, Count: Integer; + Success: Boolean; +begin + Count := 0; + if FixedSize then + begin + CollectionError(ceFixedSize); + end + else if (Index < 0) or (Index > Size) then + begin + CollectionError(ceOutOfRange); + end + else + begin + // Insert entire collection in place in correct order + NewIndex := Index; + Iterator := Collection.GetIterator; + while not Iterator.EOF do + begin + Item := Iterator.CurrentItem; + ItemError := ItemAllowed(Item); + if ItemError <> ceOK then + begin + CollectionError(ItemError); + end + else + begin + Success := Insert(NewIndex, Item); + if Success then + begin + Inc(NewIndex); + Inc(Count); + end; + end; + Iterator.Next; + end; + end; + Result := Count; +end; + +function TAbstractList.IsNilAllowed: Boolean; +begin + Result := true; +end; + +function TAbstractList.Last: ICollectable; +begin + if Size > 0 then + Result := Items[Size - 1] + else + Result := nil; +end; + +function TAbstractList.Search(const Item: ICollectable; const SearchComparator: IComparator = nil): TSearchResult; +begin + if Sorted and (SearchComparator = nil) then + Result := BinarySearch(Item) + else + Result := SequentialSearch(Item, SearchComparator); +end; + +function TAbstractList.SequentialSearch(const Item: ICollectable; const SearchComparator: IComparator): TSearchResult; +var + WorkingComparator: IComparator; + I: Integer; + Success: Boolean; +begin + if SearchComparator = nil then + WorkingComparator := Comparator + else + WorkingComparator := SearchComparator; + Result.ResultType := srNotFound; + I := 0; + Success := false; + while (I < Size) and not Success do + begin + if WorkingComparator.Equals(Item, Items[I]) then + begin + Result.ResultType := srFoundAtIndex; + Result.Index := I; + Success := true; + end + else + Inc(I); + end; +end; + +procedure TAbstractList.Sort(const SortComparator: IComparator); +begin + if SortComparator = nil then + begin + if Size > 0 then + QuickSort(0, Size - 1, Comparator); + FSorted := true; + end + else + begin + if Size > 0 then + QuickSort(0, Size - 1, SortComparator); + FSorted := false; + end; +end; + +procedure TAbstractList.Sort(CompareFunc: TCollectionCompareFunc); +begin + if Size > 0 then + QuickSort(0, Size - 1, CompareFunc); + FSorted := false; +end; + +{ TAbstractMap } +constructor TAbstractMap.Create; +begin + Create(false, true); +end; + +constructor TAbstractMap.Create(NaturalItemsOnly: Boolean); +begin + Create(NaturalItemsOnly, true); +end; + +constructor TAbstractMap.Create(NaturalItemsOnly: Boolean; NaturalKeysOnly: Boolean); +begin + inherited Create(NaturalItemsOnly); + FNaturalKeysOnly := NaturalKeysOnly or GetAlwaysNaturalKeys; + FAssociationComparator := TAssociationComparator.Create(FNaturalKeysOnly); + if FNaturalKeysOnly then + FKeyComparator := TAbstractComparator.GetNaturalComparator + else + FKeyComparator := TAbstractComparator.GetDefaultComparator; +end; + +constructor TAbstractMap.Create(const ItemArray: array of ICollectable); +begin + Create(ItemArray, true, true); +end; + +constructor TAbstractMap.Create(const ItemArray: array of ICollectable; NaturalItemsOnly: Boolean); +begin + Create(ItemArray, true, true); +end; + +constructor TAbstractMap.Create(const ItemArray: array of ICollectable; NaturalItemsOnly: Boolean; NaturalKeysOnly: Boolean); +var + I: Integer; +begin + Create(true, NaturalKeysOnly); + if not FixedSize then + begin + Capacity := Length(ItemArray); + for I := Low(ItemArray) to High(ItemArray) do + begin + Add(ItemArray[I]); + end; + end; +end; + +constructor TAbstractMap.Create(const KeyArray, ItemArray: array of ICollectable); +begin + Create(KeyArray, ItemArray, false, true); +end; + +constructor TAbstractMap.Create(const KeyArray, ItemArray: array of ICollectable; NaturalItemsOnly: Boolean); +begin + Create(KeyArray, ItemArray, NaturalItemsOnly, true); +end; + +constructor TAbstractMap.Create(const KeyArray, ItemArray: array of ICollectable; NaturalItemsOnly: Boolean; NaturalKeysOnly: Boolean); +var + I, Lo, Hi: Integer; +begin + Create(NaturalItemsOnly, NaturalKeysOnly); + if not FixedSize then + begin + Capacity := Min(Length(KeyArray), Length(ItemArray)); + Lo := Max(Low(KeyArray), Low(ItemArray)); + Hi := Min(High(KeyArray), High(ItemArray)); + for I := Lo to Hi do + begin + Put(KeyArray[I], ItemArray[I]); + end; + end; +end; + +constructor TAbstractMap.Create(const Map: IMap); +var + MapIterator: IMapIterator; +begin + Create(Map.GetNaturalItemsOnly, Map.GetNaturalKeysOnly); + InitFrom(Map); + if not FixedSize then + begin + Capacity := Map.GetSize; + MapIterator := Map.GetMapIterator; + while not MapIterator.EOF do + begin + Put(MapIterator.CurrentKey, MapIterator.CurrentItem); + MapIterator.Next; + end; + end; +end; + +destructor TAbstractMap.Destroy; +begin + FKeyComparator := nil; + FAssociationComparator := nil; + inherited Destroy; +end; + +procedure TAbstractMap.InitFrom(const Collection: ICollection); +var + Map: IMap; +begin + inherited InitFrom(Collection); + if Collection.QueryInterface(IMap, Map) = S_OK then + begin + FNaturalKeysOnly := Map.GetNaturalKeysOnly or GetAlwaysNaturalKeys; + KeyComparator := Map.GetKeyComparator; + end; +end; + +function TAbstractMap.TrueAdd(const Item: ICollectable): Boolean; +var + Position: TCollectionPosition; + Mappable: IMappable; +begin + if IsNaturalItem(Item) then + begin + Mappable := Item as IMappable; + Position := GetKeyPosition(Mappable.GetKey); + try + if Position.Found then + begin + CollectionError(ceDuplicateKey); + Result := false; + end + else + begin + TruePut(Position, TAssociation.Create(Mappable.GetKey, Item)); + Result := true; + end; + finally + Position.Free; + end; + end + else + begin + CollectionError(ceNotNaturalItem); + Result := false; + end; +end; + +function TAbstractMap.TrueContains(const Item: ICollectable): Boolean; +var + Item2: ICollectable; + Success: Boolean; + Iterator: IIterator; +begin + Iterator := GetIterator; + Success := false; + while not Iterator.EOF and not Success do + begin + Item2 := Iterator.CurrentItem; + if Comparator.Equals(Item, Item2) then + Success := true; + Iterator.Next; + end; + Result := Success; +end; + +function TAbstractMap.TrueRemove(const Item: ICollectable): ICollectable; +var + Item2: ICollectable; + Iterator: IMapIterator; + Found: Boolean; +begin + // Sequential search + Found := false; + Result := nil; + Iterator := GetAssociationIterator; + while not Iterator.EOF and not Found do + begin + Item2 := Iterator.CurrentItem; + if Comparator.Equals(Item, Item2) then + begin + Result := Item2; + Iterator.Remove; + Found := true; + end; + Iterator.Next; + end; +end; + +function TAbstractMap.TrueRemoveAll(const Item: ICollectable): ICollection; +var + ResultCollection: ICollection; + Item2: ICollectable; + Iterator: IMapIterator; +begin + // Sequential search + ResultCollection := TPArrayBag.Create(NaturalItemsOnly); + Iterator := GetAssociationIterator; + while not Iterator.EOF do + begin + Item2 := Iterator.CurrentItem; + if Comparator.Equals(Item, Item2) then + begin + ResultCollection.Add(Item2); + Iterator.Remove; + end; + Iterator.Next; + end; + Result := ResultCollection; +end; + +class function TAbstractMap.GetAlwaysNaturalKeys: Boolean; +begin + Result := false; +end; + +function TAbstractMap.GetItem(const Key: ICollectable): ICollectable; +begin + Result := Get(Key); +end; + +procedure TAbstractMap.SetItem(const Key, Item: ICollectable); +begin + Put(Key, Item); +end; + +function TAbstractMap.GetIterator: IIterator; +begin + Result := GetAssociationIterator; +end; + +function TAbstractMap.GetKeyComparator: IComparator; +begin + Result := FKeyComparator; +end; + +procedure TAbstractMap.SetKeyComparator(const Value: IComparator); +begin + FKeyComparator := Value; + FAssociationComparator.KeyComparator := Value; +end; + +function TAbstractMap.GetKeyIterator: IIterator; +begin + Result := TAssociationKeyIterator.Create(GetAssociationIterator); +end; + +function TAbstractMap.GetKeys: ISet; +var + ResultCollection: TPArraySet; + KeyIterator: IIterator; +begin + ResultCollection := TPArraySet.Create(NaturalKeysOnly); + ResultCollection.SetComparator(GetKeyComparator); + KeyIterator := GetKeyIterator; + while not KeyIterator.EOF do + begin + ResultCollection.Add(KeyIterator.CurrentItem); + KeyIterator.Next; + end; + Result := ResultCollection; +end; + +function TAbstractMap.GetMapIterator: IMapIterator; +begin + Result := GetAssociationIterator; +end; + +function TAbstractMap.GetMapIteratorByKey(const Filter: IFilter): IMapIterator; +var + Iterator: IMapIterator; +begin + Iterator := GetMapIterator; + Result := TKeyFilterMapIterator.Create(Iterator, Filter, Iterator.GetAllowRemoval); +end; + +function TAbstractMap.GetMapIteratorByKey(FilterFunc: TCollectionFilterFunc): IMapIterator; +var + Iterator: IMapIterator; +begin + Iterator := GetMapIterator; + Result := TKeyFilterFuncMapIterator.Create(Iterator, FilterFunc, Iterator.GetAllowRemoval); +end; + +function TAbstractMap.GetNaturalItemIID: TGUID; +begin + Result := MappableIID; +end; + +function TAbstractMap.GetNaturalKeyIID: TGUID; +begin + Result := EquatableIID; +end; + +function TAbstractMap.GetNaturalKeysOnly: Boolean; +begin + Result := FNaturalKeysOnly; +end; + +function TAbstractMap.GetType: TCollectionType; +begin + Result := ctMap; +end; + +function TAbstractMap.GetValues: ICollection; +var + ResultCollection: ICollection; + ValueIterator: IIterator; +begin + ResultCollection := TPArrayBag.Create(NaturalItemsOnly); + ValueIterator := GetIterator; + while not ValueIterator.EOF do + begin + ResultCollection.Add(ValueIterator.CurrentItem); + ValueIterator.Next; + end; + Result := ResultCollection; +end; + +// Overrides TAbstractCollection function, otherwise Create(ICollection) is +// called, which cannot access keys. +function TAbstractMap.Clone: ICollection; +begin + Result := (TAbstractMapClass(ClassType)).Create(Self); +end; + +function TAbstractMap.CloneAsMap: IMap; +begin + Result := (TAbstractMapClass(ClassType)).Create(Self); +end; + +function TAbstractMap.ContainsKey(const Key: ICollectable): Boolean; +var + Position: TCollectionPosition; +begin + Position := GetKeyPosition(Key); + try + Result := Position.Found; + finally + Position.Free; + end; +end; + +function TAbstractMap.ContainsKey(const KeyArray: array of ICollectable): Boolean; +var + I: Integer; + Success: Boolean; +begin + Success := true; + for I := Low(KeyArray) to High(KeyArray) do + begin + Success := Success and ContainsKey(KeyArray[I]); + if not Success then + break; + end; + Result := Success; +end; + +function TAbstractMap.ContainsKey(const Collection: ICollection): Boolean; +var + Iterator: IIterator; + Success: Boolean; +begin + Success := true; + Iterator := Collection.GetIterator; + while not Iterator.EOF do + begin + Success := Success and ContainsKey(Iterator.CurrentItem); + if not Success then + break; + Iterator.Next; + end; + Result := Success; +end; + +function TAbstractMap.Get(const Key: ICollectable): ICollectable; +var + KeyError: TCollectionError; + Position: TCollectionPosition; +begin + KeyError := KeyAllowed(Key); + if KeyError <> ceOK then + begin + CollectionError(KeyError); + Result := nil; + end + else + begin + Position := GetKeyPosition(Key); + try + if Position.Found then + Result := TrueGet(Position).GetValue + else + Result := nil; + finally + Position.Free; + end; + end; +end; + +function TAbstractMap.KeyAllowed(const Key: ICollectable): TCollectionError; +begin + if NaturalKeysOnly and not IsNaturalKey(Key) then + Result := ceNotNaturalItem + else if Key = nil then + Result := ceNilNotAllowed + else + Result := ceOK; +end; + +function TAbstractMap.IsNaturalKey(const Key: ICollectable): Boolean; +var + Temp: IUnknown; +begin + if Key.QueryInterface(NaturalKeyIID, Temp) <> E_NOINTERFACE then + Result := true + else + Result := false; +end; + +function TAbstractMap.IsNilAllowed: Boolean; +begin + Result := true; +end; + +function TAbstractMap.MatchingKey(const KeyArray: array of ICollectable): ICollection; +var + ResultCollection: ICollection; + I: Integer; +begin + ResultCollection := TPArrayBag.Create(NaturalItemsOnly); + for I := Low(KeyArray) to High(KeyArray) do + begin + if ContainsKey(KeyArray[I]) then + ResultCollection.Add(KeyArray[I]); + end; + Result := ResultCollection; +end; + +function TAbstractMap.MatchingKey(const Collection: ICollection): ICollection; +var + ResultCollection: ICollection; + Iterator: IIterator; +begin + ResultCollection := TPArrayBag.Create(NaturalItemsOnly); + Iterator := Collection.GetIterator; + while not Iterator.EOF do + begin + if ContainsKey(Iterator.CurrentItem) then + ResultCollection.Add(Iterator.CurrentItem); + Iterator.Next; + end; + Result := ResultCollection; +end; + +function TAbstractMap.Put(const Item: ICollectable): ICollectable; +var + Mappable: IMappable; + OldAssociation, NewAssociation: IAssociation; + Position: TCollectionPosition; +begin + if not IsNaturalItem(Item) then + begin + CollectionError(ceNotNaturalItem); + Result := nil; + end + else + begin + Item.QueryInterface(IMappable, Mappable); + Position := GetKeyPosition(Mappable.GetKey); + try + NewAssociation := TAssociation.Create(Mappable.GetKey, Item); + OldAssociation := TruePut(Position, NewAssociation); + if OldAssociation <> nil then + Result := OldAssociation.GetValue + else + Result := nil; + finally + Position.Free; + end; + end; +end; + +function TAbstractMap.Put(const Key, Item: ICollectable): ICollectable; +var + OldAssociation, NewAssociation: IAssociation; + ItemError, KeyError: TCollectionError; + Position: TCollectionPosition; +begin + ItemError := ItemAllowed(Item); + KeyError := KeyAllowed(Key); + if ItemError <> ceOK then + begin + CollectionError(ItemError); + Result := nil; + end + else if KeyError <> ceOK then + begin + CollectionError(KeyError); + Result := nil; + end + else + begin + // Find appropriate place, then place key-item association there + Position := GetKeyPosition(Key); + try + NewAssociation := TAssociation.Create(Key, Item); + OldAssociation := TruePut(Position, NewAssociation); + if OldAssociation <> nil then + Result := OldAssociation.GetValue + else + Result := nil; + finally + Position.Free; + end; + end; +end; + +function TAbstractMap.Put(const ItemArray: array of ICollectable): ICollection; +var + ResultCollection: ICollection; + Mappable: IMappable; + OldAssociation, NewAssociation: IAssociation; + Position: TCollectionPosition; + Item: ICollectable; + I: Integer; +begin + ResultCollection := TPArrayBag.Create(NaturalItemsOnly); + for I := Low(ItemArray) to High(ItemArray) do + begin + Item := ItemArray[I]; + if not IsNaturalItem(Item) then + begin + CollectionError(ceNotNaturalItem); + end + else + begin + // Find appropriate place, then place key-item association there + Item.QueryInterface(IMappable, Mappable); + Position := GetKeyPosition(Mappable.GetKey); + try + NewAssociation := TAssociation.Create(Mappable.GetKey, Item); + OldAssociation := TruePut(Position, NewAssociation); + if OldAssociation <> nil then + ResultCollection.Add(OldAssociation.GetValue); + finally + Position.Free; + end; + end; + end; + Result := ResultCollection; +end; + +function TAbstractMap.Put(const Collection: ICollection): ICollection; +var + ResultCollection: ICollection; + Mappable: IMappable; + OldAssociation, NewAssociation: IAssociation; + Position: TCollectionPosition; + Iterator: IIterator; + Item: ICollectable; +begin + ResultCollection := TPArrayBag.Create(NaturalItemsOnly); + Iterator := Collection.GetIterator; + while not Iterator.EOF do + begin + Item := Iterator.CurrentItem;; + if not IsNaturalItem(Item) then + begin + CollectionError(ceNotNaturalItem); + end + else + begin + // Find appropriate place, then place key-item association there + Item.QueryInterface(IMappable, Mappable); + Position := GetKeyPosition(Mappable.GetKey); + try + NewAssociation := TAssociation.Create(Mappable.GetKey, Item); + OldAssociation := TruePut(Position, NewAssociation); + if OldAssociation <> nil then + ResultCollection.Add(OldAssociation.GetValue); + finally + Position.Free; + end; + end; + Iterator.Next; + end; + Result := ResultCollection; +end; + +function TAbstractMap.Put(const Map: IMap): ICollection; +var + ResultCollection: ICollection; + OldAssociation, NewAssociation: IAssociation; + ItemError, KeyError: TCollectionError; + Position: TCollectionPosition; + MapIterator: IMapIterator; + Key, Item: ICollectable; +begin + ResultCollection := TPArrayBag.Create(NaturalItemsOnly); + MapIterator := Map.GetMapIterator; + while not MapIterator.EOF do + begin + Key := MapIterator.CurrentKey; + Item := MapIterator.CurrentItem; + + ItemError := ItemAllowed(Item); + KeyError := KeyAllowed(Key); + if ItemError <> ceOK then + begin + CollectionError(ItemError); + end + else if KeyError <> ceOK then + begin + CollectionError(KeyError); + end + else + begin + // Find appropriate place, then place key-item association there + Position := GetKeyPosition(Key); + try + NewAssociation := TAssociation.Create(Key, Item); + OldAssociation := TruePut(Position, NewAssociation); + if OldAssociation <> nil then + ResultCollection.Add(OldAssociation.GetValue); + finally + Position.Free; + end; + end; + MapIterator.Next; + end; + Result := ResultCollection; +end; + +function TAbstractMap.RemoveKey(const Key: ICollectable): ICollectable; +var + KeyError: TCollectionError; + Position: TCollectionPosition; + OldAssociation: IAssociation; +begin + KeyError := KeyAllowed(Key); + if KeyError <> ceOK then + begin + CollectionError(KeyError); + Result := nil; + end + else + begin + Position := GetKeyPosition(Key); + try + if Position.Found then + begin + OldAssociation := TrueRemove2(Position); + Result := OldAssociation.GetValue + end + else + Result := nil; + finally + Position.Free; + end; + end; +end; + +function TAbstractMap.RemoveKey(const KeyArray: array of ICollectable): ICollection; +var + ResultCollection: ICollection; + OldAssociation: IAssociation; + KeyError: TCollectionError; + Position: TCollectionPosition; + Key: ICollectable; + I: Integer; +begin + ResultCollection := TPArrayBag.Create(NaturalItemsOnly); + for I := Low(KeyArray) to High(KeyArray) do + begin + Key := KeyArray[I]; + KeyError := KeyAllowed(Key); + if KeyError <> ceOK then + begin + CollectionError(KeyError); + end + else + begin + Position := GetKeyPosition(Key); + try + if Position.Found then + begin + OldAssociation := TrueRemove2(Position); + ResultCollection.Add(OldAssociation.GetValue); + end; + finally + Position.Free; + end; + end; + end; + Result := ResultCollection; +end; + +function TAbstractMap.RemoveKey(const Collection: ICollection): ICollection; +var + ResultCollection: ICollection; + OldAssociation: IAssociation; + KeyError: TCollectionError; + Position: TCollectionPosition; + Key: ICollectable; + Iterator: IIterator; +begin + ResultCollection := TPArrayBag.Create(NaturalItemsOnly); + Iterator := Collection.GetIterator; + while not Iterator.EOF do + begin + Key := Iterator.CurrentItem; + KeyError := KeyAllowed(Key); + if KeyError <> ceOK then + begin + CollectionError(KeyError); + end + else + begin + Position := GetKeyPosition(Key); + try + if Position.Found then + begin + OldAssociation := TrueRemove2(Position); + ResultCollection.Add(OldAssociation.GetValue); + end; + finally + Position.Free; + end; + end; + Iterator.Next; + end; + Result := ResultCollection; +end; + +function TAbstractMap.RetainKey(const KeyArray: array of ICollectable): ICollection; +var + ResultCollection: ICollection; + MapIterator: IMapIterator; + I: Integer; + Found: Boolean; +begin + ResultCollection := TPArrayBag.Create(NaturalItemsOnly); + if FixedSize then + begin + CollectionError(ceFixedSize); + end + else + begin + MapIterator := GetMapIterator; + while not MapIterator.EOF do + begin + // Converting the array to a map would be faster but I don't want to + // couple base class code to a complex collection. + Found := false; + for I := Low(KeyArray) to High(KeyArray) do + begin + Found := KeyComparator.Equals(MapIterator.CurrentKey, KeyArray[I]); + if Found then + break; + end; + if not Found then + begin + ResultCollection.Add(MapIterator.CurrentItem); + MapIterator.Remove; + end; + MapIterator.Next; + end; + Result := ResultCollection; + end; +end; + +function TAbstractMap.RetainKey(const Collection: ICollection): ICollection; +var + ResultCollection: ICollection; + MapIterator: IMapIterator; + Key: ICollectable; +begin + ResultCollection := TPArrayBag.Create(NaturalItemsOnly); + if FixedSize then + begin + CollectionError(ceFixedSize); + end + else + begin + MapIterator := GetMapIterator; + while not MapIterator.EOF do + begin + Key := MapIterator.CurrentKey; + if not Collection.Contains(Key) then + begin + ResultCollection.Add(MapIterator.CurrentItem); + MapIterator.Remove; + end; + MapIterator.Next; + end; + end; + Result := ResultCollection; +end; + + +{ TAbstractIntegerMap } +constructor TAbstractIntegerMap.Create(NaturalItemsOnly: Boolean); +begin + inherited Create(NaturalItemsOnly); + FAssociationComparator := TIntegerAssociationComparator.Create; +end; + +constructor TAbstractIntegerMap.Create(const ItemArray: array of ICollectable); +begin + Create(ItemArray, true); +end; + +constructor TAbstractIntegerMap.Create(const ItemArray: array of ICollectable; NaturalItemsOnly: Boolean); +begin + inherited Create(ItemArray, true); +end; + +constructor TAbstractIntegerMap.Create(const KeyArray: array of Integer; const ItemArray: array of ICollectable); +begin + Create(KeyArray, ItemArray, false); +end; + +constructor TAbstractIntegerMap.Create(const KeyArray: array of Integer; const ItemArray: array of ICollectable; NaturalItemsOnly: Boolean); +var + I, Lo, Hi: Integer; +begin + Create(NaturalItemsOnly); + Capacity := Min(Length(KeyArray), Length(ItemArray)); + if not FixedSize then + begin + Lo := Max(Low(KeyArray), Low(ItemArray)); + Hi := Min(High(KeyArray), High(ItemArray)); + for I := Lo to Hi do + begin + Put(KeyArray[I], ItemArray[I]); + end; + end; +end; + +constructor TAbstractIntegerMap.Create(const Map: IIntegerMap); +var + MapIterator: IIntegerMapIterator; +begin + Create(Map.GetNaturalItemsOnly); + InitFrom(Map); + Capacity := Map.GetSize; + if not FixedSize then + begin + MapIterator := Map.GetMapIterator; + while not MapIterator.EOF do + begin + Put(MapIterator.CurrentKey, MapIterator.CurrentItem); + MapIterator.Next; + end; + end; +end; + +destructor TAbstractIntegerMap.Destroy; +begin + FAssociationComparator := nil; + inherited Destroy; +end; + +function TAbstractIntegerMap.TrueAdd(const Item: ICollectable): Boolean; +var + Position: TCollectionPosition; + Mappable: IIntegerMappable; +begin + if IsNaturalItem(Item) then + begin + Mappable := Item as IIntegerMappable; + Position := GetKeyPosition(Mappable.GetKey); + try + if Position.Found then + begin + CollectionError(ceDuplicateKey); + Result := false; + end + else + begin + TruePut(Position, TIntegerAssociation.Create(Mappable.GetKey, Item)); + Result := true; + end; + finally + Position.Free; + end; + end + else + begin + CollectionError(ceNotNaturalItem); + Result := false; + end; +end; + +function TAbstractIntegerMap.TrueContains(const Item: ICollectable): Boolean; +var + Item2: ICollectable; + Success: Boolean; + Iterator: IIterator; +begin + Iterator := GetIterator; + Success := false; + while not Iterator.EOF and not Success do + begin + Item2 := Iterator.CurrentItem; + if Comparator.Equals(Item, Item2) then + Success := true; + Iterator.Next; + end; + Result := Success; +end; + +function TAbstractIntegerMap.TrueRemove(const Item: ICollectable): ICollectable; +var + Item2: ICollectable; + Iterator: IIntegerMapIterator; + Found: Boolean; +begin + // Sequential search + Found := false; + Result := nil; + Iterator := GetAssociationIterator; + while not Iterator.EOF and not Found do + begin + Item2 := Iterator.CurrentItem; + if Comparator.Equals(Item, Item2) then + begin + Result := Item2; + Iterator.Remove; + Found := true; + end; + Iterator.Next; + end; +end; + +function TAbstractIntegerMap.TrueRemoveAll(const Item: ICollectable): ICollection; +var + ResultCollection: ICollection; + Item2: ICollectable; + Iterator: IIntegerMapIterator; +begin + // Sequential search + ResultCollection := TPArrayBag.Create(NaturalItemsOnly); + Iterator := GetAssociationIterator; + while not Iterator.EOF do + begin + Item2 := Iterator.CurrentItem; + if Comparator.Equals(Item, Item2) then + begin + ResultCollection.Add(Item2); + Iterator.Remove; + end; + Iterator.Next; + end; + Result := ResultCollection; +end; + +function TAbstractIntegerMap.GetItem(const Key: Integer): ICollectable; +begin + Result := Get(Key); +end; + +procedure TAbstractIntegerMap.SetItem(const Key: Integer; const Item: ICollectable); +begin + Put(Key, Item); +end; + +function TAbstractIntegerMap.GetIterator: IIterator; +begin + Result := GetAssociationIterator; +end; + +function TAbstractIntegerMap.GetKeys: ISet; +var + ResultCollection: TPArraySet; + MapIterator: IIntegerMapIterator; +begin + ResultCollection := TPArraySet.Create(true); + MapIterator := GetMapIterator; + while not MapIterator.EOF do + begin + ResultCollection.Add(TIntegerWrapper.Create(MapIterator.CurrentKey) as ICollectable); + MapIterator.Next; + end; + Result := ResultCollection; +end; + +function TAbstractIntegerMap.GetMapIterator: IIntegerMapIterator; +begin + Result := GetAssociationIterator; +end; + +function TAbstractIntegerMap.GetNaturalItemIID: TGUID; +begin + Result := IntegerMappableIID; +end; + +function TAbstractIntegerMap.GetType: TCollectionType; +begin + Result := ctIntegerMap; +end; + +function TAbstractIntegerMap.GetValues: ICollection; +var + ResultCollection: ICollection; + ValueIterator: IIterator; +begin + ResultCollection := TPArrayBag.Create(NaturalItemsOnly); + ValueIterator := GetIterator; + while not ValueIterator.EOF do + begin + ResultCollection.Add(ValueIterator.CurrentItem); + ValueIterator.Next; + end; + Result := ResultCollection; +end; + +// Overrides TAbstractCollection function, otherwise Create(ICollection) is +// called, which cannot access keys. +function TAbstractIntegerMap.Clone: ICollection; +begin + Result := (TAbstractIntegerMapClass(ClassType)).Create(Self); +end; + +function TAbstractIntegerMap.CloneAsIntegerMap: IIntegerMap; +begin + Result := (TAbstractIntegerMapClass(ClassType)).Create(Self); +end; + +function TAbstractIntegerMap.ContainsKey(const Key: Integer): Boolean; +var + Position: TCollectionPosition; +begin + Position := GetKeyPosition(Key); + try + Result := Position.Found; + finally + Position.Free; + end; +end; + +function TAbstractIntegerMap.ContainsKey(const KeyArray: array of Integer): Boolean; +var + I: Integer; + Success: Boolean; +begin + Success := true; + for I := Low(KeyArray) to High(KeyArray) do + begin + Success := Success and ContainsKey(KeyArray[I]); + if not Success then + break; + end; + Result := Success; +end; + +function TAbstractIntegerMap.Get(const Key: Integer): ICollectable; +var + Position: TCollectionPosition; +begin + Position := GetKeyPosition(Key); + try + if Position.Found then + Result := TrueGet(Position).GetValue + else + Result := nil; + finally + Position.Free; + end; +end; + +function TAbstractIntegerMap.IsNilAllowed: Boolean; +begin + Result := true; +end; + +function TAbstractIntegerMap.Put(const Item: ICollectable): ICollectable; +var + Mappable: IIntegerMappable; + OldAssociation, NewAssociation: IIntegerAssociation; + Position: TCollectionPosition; +begin + if not IsNaturalItem(Item) then + begin + CollectionError(ceNotNaturalItem); + Result := nil; + end + else + begin + Item.QueryInterface(IIntegerMappable, Mappable); + Position := GetKeyPosition(Mappable.GetKey); + try + NewAssociation := TIntegerAssociation.Create(Mappable.GetKey, Item); + OldAssociation := TruePut(Position, NewAssociation); + if OldAssociation <> nil then + Result := OldAssociation.GetValue + else + Result := nil; + finally + Position.Free; + end; + end; +end; + +function TAbstractIntegerMap.Put(const Key: Integer; const Item: ICollectable): ICollectable; +var + OldAssociation, NewAssociation: IIntegerAssociation; + ItemError: TCollectionError; + Position: TCollectionPosition; +begin + ItemError := ItemAllowed(Item); + if ItemError <> ceOK then + begin + CollectionError(ItemError); + Result := nil; + end + else + begin + Position := GetKeyPosition(Key); + try + NewAssociation := TIntegerAssociation.Create(Key, Item); + OldAssociation := TruePut(Position, NewAssociation); + if OldAssociation <> nil then + Result := OldAssociation.GetValue + else + Result := nil; + finally + Position.Free; + end; + end; +end; + +function TAbstractIntegerMap.Put(const ItemArray: array of ICollectable): ICollection; +var + ResultCollection: ICollection; + Mappable: IIntegerMappable; + OldAssociation, NewAssociation: IIntegerAssociation; + Position: TCollectionPosition; + Item: ICollectable; + I: Integer; +begin + ResultCollection := TPArrayBag.Create(NaturalItemsOnly); + for I := Low(ItemArray) to High(ItemArray) do + begin + Item := ItemArray[I]; + if not IsNaturalItem(Item) then + begin + CollectionError(ceNotNaturalItem); + end + else + begin + Item.QueryInterface(IIntegerMappable, Mappable); + Position := GetKeyPosition(Mappable.GetKey); + try + NewAssociation := TIntegerAssociation.Create(Mappable.GetKey, Item); + OldAssociation := TruePut(Position, NewAssociation); + if OldAssociation <> nil then + ResultCollection.Add(OldAssociation.GetValue); + finally + Position.Free; + end; + end; + end; + Result := ResultCollection; +end; + +function TAbstractIntegerMap.Put(const Collection: ICollection): ICollection; +var + ResultCollection: ICollection; + Mappable: IIntegerMappable; + OldAssociation, NewAssociation: IIntegerAssociation; + Position: TCollectionPosition; + Iterator: IIterator; + Item: ICollectable; +begin + ResultCollection := TPArrayBag.Create(NaturalItemsOnly); + Iterator := Collection.GetIterator; + while not Iterator.EOF do + begin + Item := Iterator.CurrentItem;; + if not IsNaturalItem(Item) then + begin + CollectionError(ceNotNaturalItem); + end + else + begin + Item.QueryInterface(IIntegerMappable, Mappable); + Position := GetKeyPosition(Mappable.GetKey); + try + NewAssociation := TIntegerAssociation.Create(Mappable.GetKey, Item); + OldAssociation := TruePut(Position, NewAssociation); + if OldAssociation <> nil then + ResultCollection.Add(OldAssociation.GetValue); + finally + Position.Free; + end; + end; + Iterator.Next; + end; + Result := ResultCollection; +end; + +function TAbstractIntegerMap.Put(const Map: IIntegerMap): ICollection; +var + ResultCollection: ICollection; + OldAssociation, NewAssociation: IIntegerAssociation; + ItemError: TCollectionError; + Position: TCollectionPosition; + MapIterator: IIntegerMapIterator; + Item: ICollectable; + Key: Integer; +begin + ResultCollection := TPArrayBag.Create(NaturalItemsOnly); + MapIterator := Map.GetMapIterator; + while not MapIterator.EOF do + begin + Key := MapIterator.CurrentKey; + Item := MapIterator.CurrentItem; + + ItemError := ItemAllowed(Item); + if ItemError <> ceOK then + begin + CollectionError(ItemError); + end + else + begin + Position := GetKeyPosition(Key); + try + NewAssociation := TIntegerAssociation.Create(Key, Item); + OldAssociation := TruePut(Position, NewAssociation); + if OldAssociation <> nil then + ResultCollection.Add(OldAssociation.GetValue); + finally + Position.Free; + end; + end; + MapIterator.Next; + end; + Result := ResultCollection; +end; + +function TAbstractIntegerMap.RemoveKey(const Key: Integer): ICollectable; +var + Position: TCollectionPosition; + OldAssociation: IIntegerAssociation; +begin + Position := GetKeyPosition(Key); + try + if Position.Found then + begin + OldAssociation := TrueRemove2(Position); + Result := OldAssociation.GetValue + end + else + Result := nil; + finally + Position.Free; + end; +end; + +function TAbstractIntegerMap.RemoveKey(const KeyArray: array of Integer): ICollection; +var + ResultCollection: ICollection; + OldAssociation: IIntegerAssociation; + Position: TCollectionPosition; + Key: Integer; + I: Integer; +begin + ResultCollection := TPArrayBag.Create(NaturalItemsOnly); + for I := Low(KeyArray) to High(KeyArray) do + begin + Key := KeyArray[I]; + Position := GetKeyPosition(Key); + try + if Position.Found then + begin + OldAssociation := TrueRemove2(Position); + ResultCollection.Add(OldAssociation.GetValue); + end; + finally + Position.Free; + end; + end; + Result := ResultCollection; +end; + +function TAbstractIntegerMap.RetainKey(const KeyArray: array of Integer): ICollection; +var + ResultCollection: ICollection; + MapIterator: IIntegerMapIterator; + I: Integer; + Found: Boolean; +begin + ResultCollection := TPArrayBag.Create(NaturalItemsOnly); + if FixedSize then + begin + CollectionError(ceFixedSize); + end + else + begin + MapIterator := GetMapIterator; + while not MapIterator.EOF do + begin + // Converting the array to a map would be faster but I don't want to + // couple base class code to a complex collection. + Found := false; + for I := Low(KeyArray) to High(KeyArray) do + begin + Found := (MapIterator.CurrentKey = KeyArray[I]); + if Found then + break; + end; + if not Found then + begin + ResultCollection.Add(MapIterator.CurrentItem); + MapIterator.Remove; + end; + MapIterator.Next; + end; + Result := ResultCollection; + end; +end; + + +{ TAbstractStringMap } +constructor TAbstractStringMap.Create(NaturalItemsOnly: Boolean); +begin + inherited Create(NaturalItemsOnly); + FAssociationComparator := TStringAssociationComparator.Create; +end; + +constructor TAbstractStringMap.Create(const ItemArray: array of ICollectable); +begin + Create(ItemArray, true); +end; + +constructor TAbstractStringMap.Create(const ItemArray: array of ICollectable; NaturalItemsOnly: Boolean); +begin + inherited Create(ItemArray, true); +end; + +constructor TAbstractStringMap.Create(const KeyArray: array of String; const ItemArray: array of ICollectable); +begin + Create(KeyArray, ItemArray, false); +end; + +constructor TAbstractStringMap.Create(const KeyArray: array of String; const ItemArray: array of ICollectable; NaturalItemsOnly: Boolean); +var + I, Lo, Hi: Integer; +begin + Create(NaturalItemsOnly); + Capacity := Min(Length(KeyArray), Length(ItemArray)); + if not FixedSize then + begin + Lo := Max(Low(KeyArray), Low(ItemArray)); + Hi := Min(High(KeyArray), High(ItemArray)); + for I := Lo to Hi do + begin + Put(KeyArray[I], ItemArray[I]); + end; + end; +end; + +constructor TAbstractStringMap.Create(const Map: IStringMap); +var + MapIterator: IStringMapIterator; +begin + Create(Map.GetNaturalItemsOnly); + InitFrom(Map); + Capacity := Map.GetSize; + if not FixedSize then + begin + MapIterator := Map.GetMapIterator; + while not MapIterator.EOF do + begin + Put(MapIterator.CurrentKey, MapIterator.CurrentItem); + MapIterator.Next; + end; + end; +end; + +destructor TAbstractStringMap.Destroy; +begin + FAssociationComparator := nil; + inherited Destroy; +end; + +function TAbstractStringMap.TrueAdd(const Item: ICollectable): Boolean; +var + Position: TCollectionPosition; + Mappable: IStringMappable; +begin + if IsNaturalItem(Item) then + begin + Mappable := Item as IStringMappable; + Position := GetKeyPosition(Mappable.GetKey); + try + if Position.Found then + begin + CollectionError(ceDuplicateKey); + Result := false; + end + else + begin + TruePut(Position, TStringAssociation.Create(Mappable.GetKey, Item)); + Result := true; + end; + finally + Position.Free; + end; + end + else + begin + CollectionError(ceNotNaturalItem); + Result := false; + end; +end; + +function TAbstractStringMap.TrueContains(const Item: ICollectable): Boolean; +var + Item2: ICollectable; + Success: Boolean; + Iterator: IIterator; +begin + Iterator := GetIterator; + Success := false; + while not Iterator.EOF and not Success do + begin + Item2 := Iterator.CurrentItem; + if Comparator.Equals(Item, Item2) then + Success := true; + Iterator.Next; + end; + Result := Success; +end; + +function TAbstractStringMap.TrueRemove(const Item: ICollectable): ICollectable; +var + Item2: ICollectable; + Iterator: IStringMapIterator; + Found: Boolean; +begin + // Sequential search + Found := false; + Result := nil; + Iterator := GetAssociationIterator; + while not Iterator.EOF and not Found do + begin + Item2 := Iterator.CurrentItem; + if Comparator.Equals(Item, Item2) then + begin + Result := Item2; + Iterator.Remove; + Found := true; + end; + Iterator.Next; + end; +end; + +function TAbstractStringMap.TrueRemoveAll(const Item: ICollectable): ICollection; +var + ResultCollection: ICollection; + Item2: ICollectable; + Iterator: IIterator; +begin + // Sequential search + ResultCollection := TPArrayBag.Create(NaturalItemsOnly); + Iterator := GetAssociationIterator; + while not Iterator.EOF do + begin + Item2 := Iterator.CurrentItem; + if Comparator.Equals(Item, Item2) then + begin + ResultCollection.Add(Item2); + Iterator.Remove; + end; + Iterator.Next; + end; + Result := ResultCollection; +end; + +function TAbstractStringMap.GetItem(const Key: String): ICollectable; +begin + Result := Get(Key); +end; + +procedure TAbstractStringMap.SetItem(const Key: String; const Item: ICollectable); +begin + Put(Key, Item); +end; + +function TAbstractStringMap.GetIterator: IIterator; +begin + Result := GetAssociationIterator; +end; + +function TAbstractStringMap.GetKeys: ISet; +var + ResultCollection: TPArraySet; + MapIterator: IStringMapIterator; +begin + ResultCollection := TPArraySet.Create(true); + MapIterator := GetMapIterator; + while not MapIterator.EOF do + begin + ResultCollection.Add(TStringWrapper.Create(MapIterator.CurrentKey) as ICollectable); + MapIterator.Next; + end; + Result := ResultCollection; +end; + +function TAbstractStringMap.GetMapIterator: IStringMapIterator; +begin + Result := GetAssociationIterator; +end; + +function TAbstractStringMap.GetNaturalItemIID: TGUID; +begin + Result := StringMappableIID; +end; + +function TAbstractStringMap.GetType: TCollectionType; +begin + Result := ctStringMap; +end; + +function TAbstractStringMap.GetValues: ICollection; +var + ResultCollection: ICollection; + ValueIterator: IIterator; +begin + ResultCollection := TPArrayBag.Create(NaturalItemsOnly); + ValueIterator := GetIterator; + while not ValueIterator.EOF do + begin + ResultCollection.Add(ValueIterator.CurrentItem); + ValueIterator.Next; + end; + Result := ResultCollection; +end; + +// Overrides TAbstractCollection function, otherwise Create(ICollection) is +// called, which cannot access keys. +function TAbstractStringMap.Clone: ICollection; +begin + Result := (TAbstractStringMapClass(ClassType)).Create(Self); +end; + +function TAbstractStringMap.CloneAsStringMap: IStringMap; +begin + Result := (TAbstractStringMapClass(ClassType)).Create(Self); +end; + +function TAbstractStringMap.ContainsKey(const Key: String): Boolean; +var + Position: TCollectionPosition; +begin + Position := GetKeyPosition(Key); + try + Result := Position.Found; + finally + Position.Free; + end; +end; + +function TAbstractStringMap.ContainsKey(const KeyArray: array of String): Boolean; +var + I: Integer; + Success: Boolean; +begin + Success := true; + for I := Low(KeyArray) to High(KeyArray) do + begin + Success := Success and ContainsKey(KeyArray[I]); + if not Success then + break; + end; + Result := Success; +end; + +function TAbstractStringMap.Get(const Key: String): ICollectable; +var + Position: TCollectionPosition; +begin + Position := GetKeyPosition(Key); + try + if Position.Found then + Result := TrueGet(Position).GetValue + else + Result := nil; + finally + Position.Free; + end; +end; + +function TAbstractStringMap.IsNilAllowed: Boolean; +begin + Result := true; +end; + +function TAbstractStringMap.Put(const Item: ICollectable): ICollectable; +var + Mappable: IStringMappable; + OldAssociation, NewAssociation: IStringAssociation; + Position: TCollectionPosition; +begin + if not IsNaturalItem(Item) then + begin + CollectionError(ceNotNaturalItem); + Result := nil; + end + else + begin + Item.QueryInterface(IStringMappable, Mappable); + Position := GetKeyPosition(Mappable.GetKey); + try + NewAssociation := TStringAssociation.Create(Mappable.GetKey, Item); + OldAssociation := TruePut(Position, NewAssociation); + if OldAssociation <> nil then + Result := OldAssociation.GetValue + else + Result := nil; + finally + Position.Free; + end; + end; +end; + +function TAbstractStringMap.Put(const Key: String; const Item: ICollectable): ICollectable; +var + OldAssociation, NewAssociation: IStringAssociation; + ItemError: TCollectionError; + Position: TCollectionPosition; +begin + ItemError := ItemAllowed(Item); + if ItemError <> ceOK then + begin + CollectionError(ItemError); + Result := nil; + end + else + begin + Position := GetKeyPosition(Key); + try + NewAssociation := TStringAssociation.Create(Key, Item); + OldAssociation := TruePut(Position, NewAssociation); + if OldAssociation <> nil then + Result := OldAssociation.GetValue + else + Result := nil; + finally + Position.Free; + end; + end; +end; + +function TAbstractStringMap.Put(const ItemArray: array of ICollectable): ICollection; +var + ResultCollection: ICollection; + Mappable: IStringMappable; + OldAssociation, NewAssociation: IStringAssociation; + Position: TCollectionPosition; + Item: ICollectable; + I: Integer; +begin + ResultCollection := TPArrayBag.Create(NaturalItemsOnly); + for I := Low(ItemArray) to High(ItemArray) do + begin + Item := ItemArray[I]; + if not IsNaturalItem(Item) then + begin + CollectionError(ceNotNaturalItem); + end + else + begin + Item.QueryInterface(IStringMappable, Mappable); + Position := GetKeyPosition(Mappable.GetKey); + try + NewAssociation := TStringAssociation.Create(Mappable.GetKey, Item); + OldAssociation := TruePut(Position, NewAssociation); + if OldAssociation <> nil then + ResultCollection.Add(OldAssociation.GetValue); + finally + Position.Free; + end; + end; + end; + Result := ResultCollection; +end; + +function TAbstractStringMap.Put(const Collection: ICollection): ICollection; +var + ResultCollection: ICollection; + Mappable: IStringMappable; + OldAssociation, NewAssociation: IStringAssociation; + Position: TCollectionPosition; + Iterator: IIterator; + Item: ICollectable; +begin + ResultCollection := TPArrayBag.Create(NaturalItemsOnly); + Iterator := Collection.GetIterator; + while not Iterator.EOF do + begin + Item := Iterator.CurrentItem;; + if not IsNaturalItem(Item) then + begin + CollectionError(ceNotNaturalItem); + end + else + begin + Item.QueryInterface(IStringMappable, Mappable); + Position := GetKeyPosition(Mappable.GetKey); + try + NewAssociation := TStringAssociation.Create(Mappable.GetKey, Item); + OldAssociation := TruePut(Position, NewAssociation); + if OldAssociation <> nil then + ResultCollection.Add(OldAssociation.GetValue); + finally + Position.Free; + end; + end; + Iterator.Next; + end; + Result := ResultCollection; +end; + +function TAbstractStringMap.Put(const Map: IStringMap): ICollection; +var + ResultCollection: ICollection; + OldAssociation, NewAssociation: IStringAssociation; + ItemError: TCollectionError; + Position: TCollectionPosition; + MapIterator: IStringMapIterator; + Item: ICollectable; + Key: String; +begin + ResultCollection := TPArrayBag.Create(NaturalItemsOnly); + MapIterator := Map.GetMapIterator; + while not MapIterator.EOF do + begin + Key := MapIterator.CurrentKey; + Item := MapIterator.CurrentItem; + + ItemError := ItemAllowed(Item); + if ItemError <> ceOK then + begin + CollectionError(ItemError); + end + else + begin + Position := GetKeyPosition(Key); + try + NewAssociation := TStringAssociation.Create(Key, Item); + OldAssociation := TruePut(Position, NewAssociation); + if OldAssociation <> nil then + ResultCollection.Add(OldAssociation.GetValue); + finally + Position.Free; + end; + end; + MapIterator.Next; + end; + Result := ResultCollection; +end; + +function TAbstractStringMap.RemoveKey(const Key: String): ICollectable; +var + Position: TCollectionPosition; + OldAssociation: IStringAssociation; +begin + Position := GetKeyPosition(Key); + try + if Position.Found then + begin + OldAssociation := TrueRemove2(Position); + Result := OldAssociation.GetValue + end + else + Result := nil; + finally + Position.Free; + end; +end; + +function TAbstractStringMap.RemoveKey(const KeyArray: array of String): ICollection; +var + ResultCollection: ICollection; + OldAssociation: IStringAssociation; + Position: TCollectionPosition; + Key: String; + I: Integer; +begin + ResultCollection := TPArrayBag.Create(NaturalItemsOnly); + for I := Low(KeyArray) to High(KeyArray) do + begin + Key := KeyArray[I]; + Position := GetKeyPosition(Key); + try + if Position.Found then + begin + OldAssociation := TrueRemove2(Position); + ResultCollection.Add(OldAssociation.GetValue); + end; + finally + Position.Free; + end; + end; + Result := ResultCollection; +end; + +function TAbstractStringMap.RetainKey(const KeyArray: array of String): ICollection; +var + ResultCollection: ICollection; + MapIterator: IStringMapIterator; + I: Integer; + Found: Boolean; +begin + ResultCollection := TPArrayBag.Create(NaturalItemsOnly); + if FixedSize then + begin + CollectionError(ceFixedSize); + end + else + begin + MapIterator := GetMapIterator; + while not MapIterator.EOF do + begin + // Converting the array to a map would be faster but I don't want to + // couple base class code to a complex collection. + Found := false; + for I := Low(KeyArray) to High(KeyArray) do + begin + Found := (MapIterator.CurrentKey = KeyArray[I]); + if Found then + break; + end; + if not Found then + begin + ResultCollection.Add(MapIterator.CurrentItem); + MapIterator.Remove; + end; + MapIterator.Next; + end; + Result := ResultCollection; + end; +end; + + +{ ECollectionError } +constructor ECollectionError.Create(const Msg: String; const Collection: ICollection; ErrorType: TCollectionError); +begin + inherited Create(Msg); + FCollection := Collection; + FErrorType := ErrorType; +end; + +{ TAbstractListIterator } +constructor TAbstractListIterator.Create(Collection: TAbstractList); +begin + inherited Create(true); + FCollection := Collection; + First; +end; + +function TAbstractListIterator.TrueFirst: ICollectable; +begin + FIndex := 0; + if FIndex < FCollection.GetSize then + Result := FCollection.GetItem(FIndex) + else + Result := nil; +end; + +function TAbstractListIterator.TrueNext: ICollectable; +begin + Inc(FIndex); + if FIndex < FCollection.GetSize then + Result := FCollection.GetItem(FIndex) + else + Result := nil; +end; + +procedure TAbstractListIterator.TrueRemove; +begin + FCollection.Delete(FIndex); + Dec(FIndex); +end; + +end. diff --git a/Lua/src/lib/collections/readme.txt b/Lua/src/lib/collections/readme.txt new file mode 100644 index 00000000..1f6477de --- /dev/null +++ b/Lua/src/lib/collections/readme.txt @@ -0,0 +1,14 @@ +Delphi Collections by Matthew Greet +http://www.warmachine.u-net.com/delphi_collections/ + +Help files (MS .hlp format) at +http://www.warmachine.u-net.com/downloads/delphi_collections_1_0_help.zip + +Changes +===================== +2008-11-06 FPC compatibility fixes by UltraStar Deluxe Team +2005-03-14 Maintenance release v1.0.5 - bug fix for sorted lists and functional tests checks unsorted and sorted lists. +2004-10-14 Maintenance release v1.0.4 - memory leak fixed. +2004-06-12 Maintenance release v1.0.3 - memory leak fixed, memory leak test, new Capacity property. +2004-02-13 Maintenance release v1.0.2 - expanded introduction and quick start sections in help file. +2003-10-25 Maintenance release v1.0.1 - packages and test harness no longer list unused packages.
\ No newline at end of file diff --git a/Lua/src/lib/ctypes/ctypes.pas b/Lua/src/lib/ctypes/ctypes.pas index 6cdf77fc..694552dc 100644 --- a/Lua/src/lib/ctypes/ctypes.pas +++ b/Lua/src/lib/ctypes/ctypes.pas @@ -1,72 +1,72 @@ -{
- This file is part of the Free Pascal run time library.
- Copyright (c) 2004 by Marco van de Voort, member of the
- Free Pascal development team
-
- Implements C types for in header conversions
-
- See the file COPYING.FPC, included in this distribution,
- for details about the copyright.
-
- 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.
-
-
- **********************************************************************}
-
-unit ctypes;
-
-interface
-
-type
- qword = int64; // Keep h2pas "uses ctypes" headers working with delphi.
-
- { the following type definitions are compiler dependant }
- { and system dependant }
-
- cint8 = shortint; pcint8 = ^cint8;
- cuint8 = byte; pcuint8 = ^cuint8;
- cchar = cint8; pcchar = ^cchar;
- cschar = cint8; pcschar = ^cschar;
- cuchar = cuint8; pcuchar = ^cuchar;
-
- cint16 = smallint; pcint16 = ^cint16;
- cuint16 = word; pcuint16 = ^cuint16;
- cshort = cint16; pcshort = ^cshort;
- csshort = cint16; pcsshort = ^csshort;
- cushort = cuint16; pcushort = ^cushort;
-
- cint32 = longint; pcint32 = ^cint32;
- cuint32 = longword; pcuint32 = ^cuint32;
- cint = cint32; pcint = ^cint; { minimum range is : 32-bit }
- csint = cint32; pcsint = ^csint; { minimum range is : 32-bit }
- cuint = cuint32; pcuint = ^cuint; { minimum range is : 32-bit }
- csigned = cint; pcsigned = ^csigned;
- cunsigned = cuint; pcunsigned = ^cunsigned;
-
- cint64 = int64; pcint64 = ^cint64;
- cuint64 = qword; pcuint64 = ^cuint64;
- clonglong = cint64; pclonglong = ^clonglong;
- cslonglong = cint64; pcslonglong = ^cslonglong;
- culonglong = cuint64; pculonglong = ^culonglong;
-
- cbool = longbool; pcbool = ^cbool;
-
-{$if defined(cpu64) and not(defined(win64) and defined(cpux86_64))}
- clong = int64; pclong = ^clong;
- cslong = int64; pcslong = ^cslong;
- culong = qword; pculong = ^culong;
-{$else}
- clong = longint; pclong = ^clong;
- cslong = longint; pcslong = ^cslong;
- culong = cardinal; pculong = ^culong;
-{$ifend}
-
- cfloat = single; pcfloat = ^cfloat;
- cdouble = double; pcdouble = ^cdouble;
- clongdouble = extended; pclongdouble = ^clongdouble;
-
-implementation
-
-end.
+{ + This file is part of the Free Pascal run time library. + Copyright (c) 2004 by Marco van de Voort, member of the + Free Pascal development team + + Implements C types for in header conversions + + See the file COPYING.FPC, included in this distribution, + for details about the copyright. + + 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. + + + **********************************************************************} + +unit ctypes; + +interface + +type + qword = int64; // Keep h2pas "uses ctypes" headers working with delphi. + + { the following type definitions are compiler dependant } + { and system dependant } + + cint8 = shortint; pcint8 = ^cint8; + cuint8 = byte; pcuint8 = ^cuint8; + cchar = cint8; pcchar = ^cchar; + cschar = cint8; pcschar = ^cschar; + cuchar = cuint8; pcuchar = ^cuchar; + + cint16 = smallint; pcint16 = ^cint16; + cuint16 = word; pcuint16 = ^cuint16; + cshort = cint16; pcshort = ^cshort; + csshort = cint16; pcsshort = ^csshort; + cushort = cuint16; pcushort = ^cushort; + + cint32 = longint; pcint32 = ^cint32; + cuint32 = longword; pcuint32 = ^cuint32; + cint = cint32; pcint = ^cint; { minimum range is : 32-bit } + csint = cint32; pcsint = ^csint; { minimum range is : 32-bit } + cuint = cuint32; pcuint = ^cuint; { minimum range is : 32-bit } + csigned = cint; pcsigned = ^csigned; + cunsigned = cuint; pcunsigned = ^cunsigned; + + cint64 = int64; pcint64 = ^cint64; + cuint64 = qword; pcuint64 = ^cuint64; + clonglong = cint64; pclonglong = ^clonglong; + cslonglong = cint64; pcslonglong = ^cslonglong; + culonglong = cuint64; pculonglong = ^culonglong; + + cbool = longbool; pcbool = ^cbool; + +{$if defined(cpu64) and not(defined(win64) and defined(cpux86_64))} + clong = int64; pclong = ^clong; + cslong = int64; pcslong = ^cslong; + culong = qword; pculong = ^culong; +{$else} + clong = longint; pclong = ^clong; + cslong = longint; pcslong = ^cslong; + culong = cardinal; pculong = ^culong; +{$ifend} + + cfloat = single; pcfloat = ^cfloat; + cdouble = double; pcdouble = ^cdouble; + clongdouble = extended; pclongdouble = ^clongdouble; + +implementation + +end. diff --git a/Lua/src/lib/ffmpeg/avcodec.pas b/Lua/src/lib/ffmpeg/avcodec.pas index 0954ee06..d0400d9a 100644 --- a/Lua/src/lib/ffmpeg/avcodec.pas +++ b/Lua/src/lib/ffmpeg/avcodec.pas @@ -27,8 +27,13 @@ (* * Conversion of libavcodec/avcodec.h * Min. version: 51.16.0, revision 6577, Sat Oct 7 15:30:46 2006 UTC - * Max. version: 52.0.0, revision 15448, Sun Sep 28 19:11:26 2008 UTC + * Max. version: 52.11.0, revision 16912, Sun Feb 1 02:00:19 2009 UTC *) +{ + * update to + * Max. version: 52.42.0, Sun Dec 6 19:20:00 2009 CET + * MiSchi +} unit avcodec; @@ -51,6 +56,7 @@ uses avutil, rational, opt, + SysUtils, {$IFDEF UNIX} BaseUnix, {$ENDIF} @@ -59,7 +65,7 @@ uses const (* Max. supported version by this header *) LIBAVCODEC_MAX_VERSION_MAJOR = 52; - LIBAVCODEC_MAX_VERSION_MINOR = 0; + LIBAVCODEC_MAX_VERSION_MINOR = 42; LIBAVCODEC_MAX_VERSION_RELEASE = 0; LIBAVCODEC_MAX_VERSION = (LIBAVCODEC_MAX_VERSION_MAJOR * VERSION_MAJOR) + (LIBAVCODEC_MAX_VERSION_MINOR * VERSION_MINOR) + @@ -84,9 +90,9 @@ const {$IFEND} const - AV_NOPTS_VALUE: cint64 = $8000000000000000; + AV_NOPTS_VALUE: cint64 = $8000000000000000; AV_TIME_BASE = 1000000; - AV_TIME_BASE_Q : TAVRational = (num: 1; den: AV_TIME_BASE); + AV_TIME_BASE_Q: TAVRational = (num: 1; den: AV_TIME_BASE); (** * Identifies the syntax and semantics of the bitstream. @@ -231,6 +237,35 @@ type CODEC_ID_CMV, CODEC_ID_MOTIONPIXELS, CODEC_ID_TGV, + CODEC_ID_TGQ, +{$IF LIBAVCODEC_VERSION >= 52012000} // >= 52.12.0 + CODEC_ID_TQI, +{$IFEND} +{$IF LIBAVCODEC_VERSION >= 52022002} // >= 52.22.2 + CODEC_ID_AURA, + CODEC_ID_AURA2, +{$IFEND} +{$IF LIBAVCODEC_VERSION >= 52027000} // >= 52.27.0 + CODEC_ID_V210X, +{$IFEND} +{$IF LIBAVCODEC_VERSION >= 52028000} // >= 52.28.0 + CODEC_ID_TMV, +{$IFEND} +{$IF LIBAVCODEC_VERSION >= 52029000} // >= 52.29.0 + CODEC_ID_V210, +{$IFEND} +{$IF LIBAVCODEC_VERSION >= 52030002} // >= 52.30.2 + CODEC_ID_DPX, +{$IFEND} +{$IF LIBAVCODEC_VERSION >= 52031002} // >= 52.31.2 + CODEC_ID_MAD, +{$IFEND} +{$IF LIBAVCODEC_VERSION >= 52037000} // >= 52.37.0 + CODEC_ID_FRWU, +{$IFEND} +{$IF LIBAVCODEC_VERSION >= 52041000} // >= 52.41.0 + CODEC_ID_FLASHSV2, +{$IFEND} //* various PCM "codecs" */ CODEC_ID_PCM_S16LE= $10000, @@ -257,6 +292,9 @@ type CODEC_ID_PCM_F32LE, CODEC_ID_PCM_F64BE, CODEC_ID_PCM_F64LE, +{$IF LIBAVCODEC_VERSION >= 52034000} // >= 52.34.0 + CODEC_ID_PCM_BLURAY, +{$IFEND} //* various ADPCM codecs */ CODEC_ID_ADPCM_IMA_QT= $11000, @@ -286,6 +324,7 @@ type CODEC_ID_ADPCM_IMA_EA_EACS, CODEC_ID_ADPCM_EA_XAS, CODEC_ID_ADPCM_EA_MAXIS_XA, + CODEC_ID_ADPCM_IMA_ISS, //* AMR */ CODEC_ID_AMR_NB= $12000, @@ -305,7 +344,7 @@ type CODEC_ID_MP2= $15000, CODEC_ID_MP3, ///< preferred ID for decoding MPEG audio layer 1, 2 or 3 CODEC_ID_AAC, - {$IF LIBAVCODEC_VERSION < 52000000} // 52.0.0 + {$IF LIBAVCODEC_VERSION < 52000000} // < 52.0.0 _CODEC_ID_MPEG4AAC, // will be redefined to CODEC_ID_AAC below {$IFEND} CODEC_ID_AC3, @@ -350,6 +389,19 @@ type CODEC_ID_ATRAC3P, CODEC_ID_EAC3, CODEC_ID_SIPR, + CODEC_ID_MP1, +{$IF LIBAVCODEC_VERSION >= 52020000} // >= 52.20.0 + CODEC_ID_TWINVQ, +{$IFEND} +{$IF LIBAVCODEC_VERSION >= 52022000} // >= 52.22.0 + CODEC_ID_TRUEHD, +{$IFEND} +{$IF LIBAVCODEC_VERSION >= 52026000} // >= 52.26.0 + CODEC_ID_MP4ALS, +{$IFEND} +{$IF LIBAVCODEC_VERSION >= 52035000} // >= 52.35.0 + CODEC_ID_ATRAC1, +{$IFEND} //* subtitle codecs */ CODEC_ID_DVD_SUBTITLE= $17000, @@ -358,6 +410,12 @@ type CODEC_ID_XSUB, CODEC_ID_SSA, CODEC_ID_MOV_TEXT, +{$IF LIBAVCODEC_VERSION >= 52033000} // >= 52.33.0 + CODEC_ID_HDMV_PGS_SUBTITLE, +{$IFEND} +{$IF LIBAVCODEC_VERSION >= 52037001} // >= 52.37.1 + CODEC_ID_DVB_TELETEXT, +{$IFEND} (* other specific kind of codecs (generally used for attachments) *) CODEC_ID_TTF= $18000, @@ -365,11 +423,11 @@ type CODEC_ID_PROBE= $19000, ///< codec_id is not known (like CODEC_ID_NONE) but lavf should attempt to identify it CODEC_ID_MPEG2TS= $20000, {*< _FAKE_ codec to indicate a raw MPEG-2 TS - * stream (only used by libavformat) *} + * stream (only used by libavformat) *} __CODEC_ID_4BYTE = $FFFFF // ensure 4-byte enum ); -{$IF LIBAVCODEC_VERSION < 52000000} // 52.0.0 +{$IF LIBAVCODEC_VERSION < 52000000} // < 52.0.0 {* CODEC_ID_MP3LAME is obsolete *} const CODEC_ID_MP3LAME = CODEC_ID_MP3; @@ -404,6 +462,67 @@ type PSampleFormatArray = ^_TSampleFormatArray; const + {* Audio channel masks *} + CH_FRONT_LEFT = $00000001; + CH_FRONT_RIGHT = $00000002; + CH_FRONT_CENTER = $00000004; + CH_LOW_FREQUENCY = $00000008; + CH_BACK_LEFT = $00000010; + CH_BACK_RIGHT = $00000020; + CH_FRONT_LEFT_OF_CENTER = $00000040; + CH_FRONT_RIGHT_OF_CENTER = $00000080; + CH_BACK_CENTER = $00000100; + CH_SIDE_LEFT = $00000200; + CH_SIDE_RIGHT = $00000400; + CH_TOP_CENTER = $00000800; + CH_TOP_FRONT_LEFT = $00001000; + CH_TOP_FRONT_CENTER = $00002000; + CH_TOP_FRONT_RIGHT = $00004000; + CH_TOP_BACK_LEFT = $00008000; + CH_TOP_BACK_CENTER = $00010000; + CH_TOP_BACK_RIGHT = $00020000; + CH_STEREO_LEFT = $20000000; ///< Stereo downmix. + CH_STEREO_RIGHT = $40000000; ///< See CH_STEREO_LEFT. +{** Channel mask value used for AVCodecContext.request_channel_layout + * to indicate that the user requests the channel order of the decoder output + * to be the native codec channel order. + *} +{$IF LIBAVCODEC_VERSION >= 52038001} // >= 52.38.1 + CH_LAYOUT_NATIVE = $8000000000000000LL +{$IFEND} + {* Audio channel convenience macros *} + CH_LAYOUT_MONO = (CH_FRONT_CENTER); + CH_LAYOUT_STEREO = (CH_FRONT_LEFT or CH_FRONT_RIGHT); + CH_LAYOUT_SURROUND = (CH_LAYOUT_STEREO or CH_FRONT_CENTER); +{$IF LIBAVCODEC_VERSION >= 52027000} // >= 52.27.0 + CH_LAYOUT_2_1 = (CH_LAYOUT_STEREO or CH_BACK_CENTER); + CH_LAYOUT_4POINT0 = (CH_LAYOUT_SURROUND or CH_BACK_CENTER); + CH_LAYOUT_2_2 = (CH_LAYOUT_STEREO or CH_SIDE_LEFT or CH_SIDE_RIGHT); +{$IFEND} + CH_LAYOUT_QUAD = (CH_LAYOUT_STEREO or CH_BACK_LEFT or CH_BACK_RIGHT); + CH_LAYOUT_5POINT0 = (CH_LAYOUT_SURROUND or CH_SIDE_LEFT or CH_SIDE_RIGHT); + CH_LAYOUT_5POINT1 = (CH_LAYOUT_5POINT0 or CH_LOW_FREQUENCY); +{$IF LIBAVCODEC_VERSION >= 52025000} // >= 52.25.0 + CH_LAYOUT_5POINT0_BACK = (CH_LAYOUT_SURROUND or CH_BACK_LEFT or + CH_BACK_RIGHT); + CH_LAYOUT_5POINT1_BACK = (CH_LAYOUT_5POINT0_BACK or CH_LOW_FREQUENCY); +{$IFEND} +{$IF LIBAVCODEC_VERSION >= 52034000} // >= 52.34.0 + CH_LAYOUT_7POINT0 = (CH_LAYOUT_5POINT0 or CH_BACK_LEFT or CH_BACK_RIGHT); +{$IFEND} + CH_LAYOUT_7POINT1 = (CH_LAYOUT_5POINT1 or CH_BACK_LEFT or CH_BACK_RIGHT); +{$IF LIBAVCODEC_VERSION < 52025000} // < 52.25.0 + CH_LAYOUT_7POINT1_WIDE = (CH_LAYOUT_SURROUND or CH_LOW_FREQUENCY or + CH_BACK_LEFT or CH_BACK_RIGHT or +{$ELSE} + CH_LAYOUT_7POINT1_WIDE = (CH_LAYOUT_5POINT1_BACK or +{$IFEND} + CH_FRONT_LEFT_OF_CENTER or + CH_FRONT_RIGHT_OF_CENTER); + CH_LAYOUT_STEREO_DOWNMIX = (CH_STEREO_LEFT or CH_STEREO_RIGHT); + + +const {* in bytes *} AVCODEC_MAX_AUDIO_FRAME_SIZE = 192000; // 1 second of 48khz 32bit audio @@ -441,20 +560,76 @@ type TAVDiscard = ( {* We leave some space between them for extensions (drop some - * keyframes for intra-only or drop just some bidir frames). *} - AVDISCARD_NONE =-16, ///< discard nothing - AVDISCARD_DEFAULT= 0, ///< discard useless packets like 0 size packets in avi - AVDISCARD_NONREF = 8, ///< discard all non reference - AVDISCARD_BIDIR = 16, ///< discard all bidirectional frames - AVDISCARD_NONKEY = 32, ///< discard all frames except keyframes - AVDISCARD_ALL = 48 ///< discard all + * keyframes for intra-only or drop just some bidir frames). + *} + AVDISCARD_NONE = -16, ///< discard nothing + AVDISCARD_DEFAULT = 0, ///< discard useless packets like 0 size packets in avi + AVDISCARD_NONREF = 8, ///< discard all non reference + AVDISCARD_BIDIR = 16, ///< discard all bidirectional frames + AVDISCARD_NONKEY = 32, ///< discard all frames except keyframes + AVDISCARD_ALL = 48 ///< discard all + ); + +{$IF LIBAVCODEC_VERSION >= 52028000} // >= 52.28.0 + TAVColorPrimaries = ( + AVCOL_PRI_BT709 = 1, ///< also ITU-R BT1361 / IEC 61966-2-4 / SMPTE RP177 Annex B + AVCOL_PRI_UNSPECIFIED = 2, + AVCOL_PRI_BT470M = 4, + AVCOL_PRI_BT470BG = 5, ///< also ITU-R BT601-6 625 / ITU-R BT1358 625 / ITU-R BT1700 625 PAL & SECAM + AVCOL_PRI_SMPTE170M = 6, ///< also ITU-R BT601-6 525 / ITU-R BT1358 525 / ITU-R BT1700 NTSC + AVCOL_PRI_SMPTE240M = 7, ///< functionally identical to above + AVCOL_PRI_FILM = 8, + AVCOL_PRI_NB ///< Not part of ABI + ); + + TAVColorTransferCharacteristic = ( + AVCOL_TRC_BT709 = 1, ///< also ITU-R BT1361 + AVCOL_TRC_UNSPECIFIED = 2, + AVCOL_TRC_GAMMA22 = 4, ///< also ITU-R BT470M / ITU-R BT1700 625 PAL & SECAM + AVCOL_TRC_GAMMA28 = 5, ///< also ITU-R BT470BG + AVCOL_TRC_NB ///< Not part of ABI + ); + + TAVColorSpace = ( + AVCOL_SPC_RGB = 0, + AVCOL_SPC_BT709 = 1, ///< also ITU-R BT1361 / IEC 61966-2-4 xvYCC709 / SMPTE RP177 Annex B + AVCOL_SPC_UNSPECIFIED = 2, + AVCOL_SPC_FCC = 4, + AVCOL_SPC_BT470BG = 5, ///< also ITU-R BT601-6 625 / ITU-R BT1358 625 / ITU-R BT1700 625 PAL & SECAM / IEC 61966-2-4 xvYCC601 + AVCOL_SPC_SMPTE170M = 6, ///< also ITU-R BT601-6 525 / ITU-R BT1358 525 / ITU-R BT1700 NTSC / functionally identical to above + AVCOL_SPC_SMPTE240M = 7, + AVCOL_SPC_NB ///< Not part of ABI + ); + + TAVColorRange = ( + AVCOL_RANGE_UNSPECIFIED = 0, + AVCOL_RANGE_MPEG = 1, ///< the normal 219*2^(n-8) "MPEG" YUV ranges + AVCOL_RANGE_JPEG = 2, ///< the normal 2^n-1 "JPEG" YUV ranges + AVCOL_RANGE_NB ///< Not part of ABI + ); + +(** + * X X 3 4 X X are luma samples, + * 1 2 1-6 are possible chroma positions + * X X 5 6 X 0 is undefined/unknown position + *) + TAVChromaLocation = ( + AVCHROMA_LOC_UNSPECIFIED = 0, + AVCHROMA_LOC_LEFT = 1, ///< mpeg2/4, h264 default + AVCHROMA_LOC_CENTER = 2, ///< mpeg1, jpeg, h263 + AVCHROMA_LOC_TOPLEFT = 3, ///< DV + AVCHROMA_LOC_TOP = 4, + AVCHROMA_LOC_BOTTOMLEFT = 5, + AVCHROMA_LOC_BOTTOM = 6, + AVCHROMA_LOC_NB ///< Not part of ABI ); +{$IFEND} PRcOverride = ^TRcOverride; TRcOverride = record {16} - start_frame: cint; - end_frame: cint; - qscale: cint; // if this is 0 then quality_factor will be used instead + start_frame: cint; + end_frame: cint; + qscale: cint; // if this is 0 then quality_factor will be used instead quality_factor: cfloat; end; @@ -559,6 +734,18 @@ const *) CODEC_CAP_SMALL_LAST_FRAME = $0040; + (** + * Codec can export data for HW decoding (VDPAU). + *) + CODEC_CAP_HWACCEL_VDPAU = $0080; + + {$IF LIBAVCODEC_VERSION >= 52035000} // >= 52.35.0 + (** + * Codec can output multiple frames per AVPacket + *) + CODEC_CAP_SUBFRAMES = $0100; + {$IFEND} + //the following defines may change, don't expect compatibility if you use them MB_TYPE_INTRA4x4 = $001; MB_TYPE_INTRA16x16 = $002; //FIXME h264 specific @@ -609,8 +796,8 @@ type end; const - FF_QSCALE_TYPE_MPEG1 = 0; - FF_QSCALE_TYPE_MPEG2 = 1; + FF_QSCALE_TYPE_MPEG1 = 0; + FF_QSCALE_TYPE_MPEG2 = 1; FF_QSCALE_TYPE_H264 = 2; FF_BUFFER_TYPE_INTERNAL = 1; @@ -632,7 +819,311 @@ const FF_BUFFER_HINTS_PRESERVE = $04; // User must not alter buffer content FF_BUFFER_HINTS_REUSABLE = $08; // Codec will reuse the buffer (update) +const + {$IF LIBAVCODEC_VERSION < 52000000} // < 52.0.0 + DEFAULT_FRAME_RATE_BASE = 1001000; + {$IFEND} + + FF_ASPECT_EXTENDED = 15; + + FF_RC_STRATEGY_XVID = 1; + + FF_BUG_AUTODETECT = 1; ///< autodetection + FF_BUG_OLD_MSMPEG4 = 2; + FF_BUG_XVID_ILACE = 4; + FF_BUG_UMP4 = 8; + FF_BUG_NO_PADDING = 16; + FF_BUG_AMV = 32; + FF_BUG_AC_VLC = 0; ///< will be removed, libavcodec can now handle these non compliant files by default + FF_BUG_QPEL_CHROMA = 64; + FF_BUG_STD_QPEL = 128; + FF_BUG_QPEL_CHROMA2 = 256; + FF_BUG_DIRECT_BLOCKSIZE = 512; + FF_BUG_EDGE = 1024; + FF_BUG_HPEL_CHROMA = 2048; + FF_BUG_DC_CLIP = 4096; + FF_BUG_MS = 8192; ///< workaround various bugs in microsofts broken decoders + //FF_BUG_FAKE_SCALABILITY = 16 //Autodetection should work 100%. + + FF_COMPLIANCE_VERY_STRICT = 2; ///< strictly conform to a older more strict version of the spec or reference software + FF_COMPLIANCE_STRICT = 1; ///< strictly conform to all the things in the spec no matter what consequences + FF_COMPLIANCE_NORMAL = 0; + FF_COMPLIANCE_INOFFICIAL = -1; ///< allow inofficial extensions + FF_COMPLIANCE_EXPERIMENTAL = -2; ///< allow non standarized experimental things + + FF_ER_CAREFUL = 1; + FF_ER_COMPLIANT = 2; + FF_ER_AGGRESSIVE = 3; + FF_ER_VERY_AGGRESSIVE = 4; + + FF_DCT_AUTO = 0; + FF_DCT_FASTINT = 1; + FF_DCT_INT = 2; + FF_DCT_MMX = 3; + FF_DCT_MLIB = 4; + FF_DCT_ALTIVEC = 5; + FF_DCT_FAAN = 6; + + FF_IDCT_AUTO = 0; + FF_IDCT_INT = 1; + FF_IDCT_SIMPLE = 2; + FF_IDCT_SIMPLEMMX = 3; + FF_IDCT_LIBMPEG2MMX = 4; + FF_IDCT_PS2 = 5; + FF_IDCT_MLIB = 6; + FF_IDCT_ARM = 7; + FF_IDCT_ALTIVEC = 8; + FF_IDCT_SH4 = 9; + FF_IDCT_SIMPLEARM = 10; + FF_IDCT_H264 = 11; + FF_IDCT_VP3 = 12; + FF_IDCT_IPP = 13; + FF_IDCT_XVIDMMX = 14; + FF_IDCT_CAVS = 15; + FF_IDCT_SIMPLEARMV5TE= 16; + FF_IDCT_SIMPLEARMV6 = 17; + FF_IDCT_SIMPLEVIS = 18; + FF_IDCT_WMV2 = 19; + FF_IDCT_FAAN = 20; + FF_IDCT_EA = 21; + FF_IDCT_SIMPLENEON = 22; + FF_IDCT_SIMPLEALPHA = 23; + + FF_EC_GUESS_MVS = 1; + FF_EC_DEBLOCK = 2; + + FF_MM_FORCE = $80000000; (* force usage of selected flags (OR) *) + (* lower 16 bits - CPU features *) + FF_MM_MMX = $0001; ///< standard MMX + FF_MM_3DNOW = $0004; ///< AMD 3DNOW + {$IF LIBAVCODEC_MAX_VERSION_MAJOR < 53} + FF_MM_MMXEXT = $0002; ///< SSE integer functions or AMD MMX ext + {$IFEND} + {$IF LIBAVCODEC_VERSION >= 52024000} // >= 52.24.0 + FF_MM_MMX2 = $0002; ///< SSE integer functions or AMD MMX ext + {$IFEND} + FF_MM_SSE = $0008; ///< SSE functions + FF_MM_SSE2 = $0010; ///< PIV SSE2 functions + FF_MM_3DNOWEXT = $0020; ///< AMD 3DNowExt + FF_MM_SSE3 = $0040; ///< Prescott SSE3 functions + FF_MM_SSSE3 = $0080; ///< Conroe SSSE3 functions + {$IF LIBAVCODEC_VERSION >= 52022003} // >= 52.22.3 + FF_MM_SSE4 = $0100; ///< Penryn SSE4.1 functions + FF_MM_SSE42 = $0200; ///< Nehalem SSE4.2 functions + {$IFEND} + FF_MM_IWMMXT = $0100; ///< XScale IWMMXT + FF_MM_ALTIVEC = $0001; ///< standard AltiVec + + FF_PRED_LEFT = 0; + FF_PRED_PLANE = 1; + FF_PRED_MEDIAN = 2; + + FF_DEBUG_PICT_INFO = 1; + FF_DEBUG_RC = 2; + FF_DEBUG_BITSTREAM = 4; + FF_DEBUG_MB_TYPE = 8; + FF_DEBUG_QP = 16; + FF_DEBUG_MV = 32; + FF_DEBUG_DCT_COEFF = $00000040; + FF_DEBUG_SKIP = $00000080; + FF_DEBUG_STARTCODE = $00000100; + FF_DEBUG_PTS = $00000200; + FF_DEBUG_ER = $00000400; + FF_DEBUG_MMCO = $00000800; + FF_DEBUG_BUGS = $00001000; + FF_DEBUG_VIS_QP = $00002000; + FF_DEBUG_VIS_MB_TYPE = $00004000; + FF_DEBUG_BUFFERS = $00008000; + + FF_DEBUG_VIS_MV_P_FOR = $00000001; //visualize forward predicted MVs of P frames + FF_DEBUG_VIS_MV_B_FOR = $00000002; //visualize forward predicted MVs of B frames + FF_DEBUG_VIS_MV_B_BACK = $00000004; //visualize backward predicted MVs of B frames + + FF_CMP_SAD = 0; + FF_CMP_SSE = 1; + FF_CMP_SATD = 2; + FF_CMP_DCT = 3; + FF_CMP_PSNR = 4; + FF_CMP_BIT = 5; + FF_CMP_RD = 6; + FF_CMP_ZERO = 7; + FF_CMP_VSAD = 8; + FF_CMP_VSSE = 9; + FF_CMP_NSSE = 10; + FF_CMP_W53 = 11; + FF_CMP_W97 = 12; + FF_CMP_DCTMAX = 13; + FF_CMP_DCT264 = 14; + FF_CMP_CHROMA = 256; + + FF_DTG_AFD_SAME = 8; + FF_DTG_AFD_4_3 = 9; + FF_DTG_AFD_16_9 = 10; + FF_DTG_AFD_14_9 = 11; + FF_DTG_AFD_4_3_SP_14_9 = 13; + FF_DTG_AFD_16_9_SP_14_9 = 14; + FF_DTG_AFD_SP_4_3 = 15; + + FF_DEFAULT_QUANT_BIAS = 999999; + + FF_LAMBDA_SHIFT = 7; + FF_LAMBDA_SCALE = (1 shl FF_LAMBDA_SHIFT); + FF_QP2LAMBDA = 118; ///< factor to convert from H.263 QP to lambda + FF_LAMBDA_MAX = (256 * 128 - 1); + + FF_QUALITY_SCALE = FF_LAMBDA_SCALE; //FIXME maybe remove + + FF_CODER_TYPE_VLC = 0; + FF_CODER_TYPE_AC = 1; + FF_CODER_TYPE_RAW = 2; + FF_CODER_TYPE_RLE = 3; + FF_CODER_TYPE_DEFLATE = 4; + + SLICE_FLAG_CODED_ORDER = $0001; ///< draw_horiz_band() is called in coded order instead of display + SLICE_FLAG_ALLOW_FIELD = $0002; ///< allow draw_horiz_band() with field slices (MPEG2 field pics) + SLICE_FLAG_ALLOW_PLANE = $0004; ///< allow draw_horiz_band() with 1 component at a time (SVQ1) + + FF_MB_DECISION_SIMPLE = 0; ///< uses mb_cmp + FF_MB_DECISION_BITS = 1; ///< chooses the one which needs the fewest bits + FF_MB_DECISION_RD = 2; ///< rate distortion + + FF_AA_AUTO = 0; + FF_AA_FASTINT = 1; //not implemented yet + FF_AA_INT = 2; + FF_AA_FLOAT = 3; + + FF_PROFILE_UNKNOWN = -99; + FF_PROFILE_AAC_MAIN = 0; + FF_PROFILE_AAC_LOW = 1; + FF_PROFILE_AAC_SSR = 2; + FF_PROFILE_AAC_LTP = 3; + + FF_LEVEL_UNKNOWN = -99; + + X264_PART_I4X4 = $001; (* Analyse i4x4 *) + X264_PART_I8X8 = $002; (* Analyse i8x8 (requires 8x8 transform) *) + X264_PART_P8X8 = $010; (* Analyse p16x8, p8x16 and p8x8 *) + X264_PART_P4X4 = $020; (* Analyse p8x4, p4x8, p4x4 *) + X264_PART_B8X8 = $100; (* Analyse b16x8, b8x16 and b8x8 *) + + FF_COMPRESSION_DEFAULT = -1; + +const + AVPALETTE_SIZE = 1024; + AVPALETTE_COUNT = 256; + +{$IF LIBAVCODEC_MAX_VERSION_MAJOR < 53} +type +(** + * AVPaletteControl + * This structure defines a method for communicating palette changes + * between and demuxer and a decoder. + * + * @deprecated Use AVPacket to send palette changes instead. + * This is totally broken. + *) + PAVPaletteControl = ^TAVPaletteControl; + TAVPaletteControl = record + (* demuxer sets this to 1 to indicate the palette has changed; + * decoder resets to 0 *) + palette_changed: cint; + + (* 4-byte ARGB palette entries, stored in native byte order; note that + * the individual palette components should be on a 8-bit scale; if + * the palette data comes from a IBM VGA native format, the component + * data is probably 6 bits in size and needs to be scaled *) + palette: array [0..AVPALETTE_COUNT - 1] of cuint; + end; {deprecated;} +{$IFEND} + +{$IF LIBAVCODEC_VERSION >= 52023000} // >= 52.23.0 +type + PAVPacket = ^TAVPacket; + TAVPacket = record +(* + * Presentation timestamp in AVStream->time_base units; the time at which + * the decompressed packet will be presented to the user. + * Can be AV_NOPTS_VALUE if it is not stored in the file. + * pts MUST be larger or equal to dts as presentation cannot happen before + * decompression, unless one wants to view hex dumps. Some formats misuse + * the terms dts and pts/cts to mean something different. Such timestamps + * must be converted to true pts/dts before they are stored in AVPacket. + *) + pts: cint64; +(* + * Decompression timestamp in AVStream->time_base units; the time at which + * the packet is decompressed. + * Can be AV_NOPTS_VALUE if it is not stored in the file. + *) + dts: cint64; + data: PByteArray; + size: cint; + stream_index: cint; + flags: cint; +(* + * Duration of this packet in AVStream->time_base units, 0 if unknown. + * Equals next_pts - this_pts in presentation order. + *) + duration: cint; + destruct: procedure (para1: PAVPacket); cdecl; + priv: pointer; + pos: cint64; // byte position in stream, -1 if unknown + +(* + * Time difference in AVStream->time_base units from the pts of this + * packet to the point at which the output from the decoder has converged + * independent from the availability of previous frames. That is, the + * frames are virtually identical no matter if decoding started from + * the very first frame or from this keyframe. + * Is AV_NOPTS_VALUE if unknown. + * This field is not the display duration of the current packet. + * + * The purpose of this field is to allow seeking in streams that have no + * keyframes in the conventional sense. It corresponds to the + * recovery point SEI in H.264 and match_time_delta in NUT. It is also + * essential for some types of subtitle streams to ensure that all + * subtitles are correctly displayed after seeking. + *) + convergence_duration: cint64; + end; + +const + {$IF LIBAVCODEC_VERSION >= 52030002} // >= 52.30.2 + PKT_FLAG_KEY = $0001; + {$ELSE} + AV_PKT_FLAG_KEY = $0001; + {$IF LIBAVCODEC_VERSION_MAJOR < 53} + PKT_FLAG_KEY = AV_PKT_FLAG_KEY; + {$IFEND} + {$IFEND} +{$IFEND} + type + PAVClass = ^TAVClass; {const} + PAVCodecContext = ^TAVCodecContext; + + PAVCodec = ^TAVCodec; + +{$IF LIBAVCODEC_VERSION >= 52018000} // >= 52.18.0 + PAVHWAccel = ^TAVHWAccel; +{$IFEND} + + // int[4] + PQuadIntArray = ^TQuadIntArray; + TQuadIntArray = array[0..3] of cint; + // int (*func)(struct AVCodecContext *c2, void *arg) + TExecuteFunc = function(c2: PAVCodecContext; arg: Pointer): cint; cdecl; + + TAVClass = record + class_name: PAnsiChar; + (* actually passing a pointer to an AVCodecContext + or AVFormatContext, which begin with an AVClass. + Needed because av_log is in libavcodec and has no visibility + of AVIn/OutputFormat *) + item_name: function(): PAnsiChar; cdecl; + option: PAVOption; + end; + {** * Audio Video Frame. * New fields can be added to the end of FF_COMMON_FRAME with minor version @@ -678,7 +1169,7 @@ type * - decoding: Set by libavcodec. *) pts: cint64; - (**\ + (** * picture number in bitstream order * - encoding: set by * - decoding: Set by libavcodec. @@ -707,6 +1198,7 @@ type * is this picture used as reference * The values for this are the same as the MpegEncContext.picture_structure * variable, that is 1->top field, 2->bottom field, 3->frame/both fields. + * Set to 4 for delayed, non-reference frames. * - encoding: unused * - decoding: Set by libavcodec. (before get_buffer() call)). *) @@ -831,7 +1323,7 @@ type *) ref_index: array [0..1] of PShortint; - {$IF LIBAVCODEC_VERSION >= 51068000} // 51.68.0 + {$IF LIBAVCODEC_VERSION >= 51068000} // >= 51.68.0 (** * reordered opaque 64bit number (generally a PTS) from AVCodecContext.reordered_opaque * output in AVFrame.reordered_opaque @@ -840,8 +1332,20 @@ type *) reordered_opaque: cint64; {$IFEND} + + {$IF LIBAVCODEC_VERSION = 52021000} // = 52.21.0 + (** + * hardware accelerator private data (FFmpeg allocated) + * - encoding: unused + * - decoding: Set by libavcodec + *) + hwaccel_data_private: pointer; + {$IFEND} + {$IF LIBAVCODEC_VERSION >= 52022000} // >= 52.22.0 + hwaccel_picture_private: pointer; + {$IFEND} - {$IF LIBAVCODEC_VERSION >= 51070000} // 51.70.0 + {$IF LIBAVCODEC_VERSION >= 51070000} // >= 51.70.0 (** * Bits per sample/pixel of internal libavcodec pixel/sample format. * This field is applicable only when sample_fmt is SAMPLE_FMT_S32. @@ -850,230 +1354,105 @@ type *) bits_per_raw_sample: cint; {$IFEND} - end; - -const - {$IF LIBAVCODEC_VERSION < 52000000} // < 52.0.0 - DEFAULT_FRAME_RATE_BASE = 1001000; - {$IFEND} - - FF_ASPECT_EXTENDED = 15; - - FF_RC_STRATEGY_XVID = 1; - - FF_BUG_AUTODETECT = 1; ///< autodetection - FF_BUG_OLD_MSMPEG4 = 2; - FF_BUG_XVID_ILACE = 4; - FF_BUG_UMP4 = 8; - FF_BUG_NO_PADDING = 16; - FF_BUG_AMV = 32; - FF_BUG_AC_VLC = 0; ///< will be removed, libavcodec can now handle these non compliant files by default - FF_BUG_QPEL_CHROMA = 64; - FF_BUG_STD_QPEL = 128; - FF_BUG_QPEL_CHROMA2 = 256; - FF_BUG_DIRECT_BLOCKSIZE = 512; - FF_BUG_EDGE = 1024; - FF_BUG_HPEL_CHROMA = 2048; - FF_BUG_DC_CLIP = 4096; - FF_BUG_MS = 8192; ///< workaround various bugs in microsofts broken decoders - //FF_BUG_FAKE_SCALABILITY = 16 //Autodetection should work 100%. - - FF_COMPLIANCE_VERY_STRICT = 2; ///< strictly conform to a older more strict version of the spec or reference software - FF_COMPLIANCE_STRICT = 1; ///< strictly conform to all the things in the spec no matter what consequences - FF_COMPLIANCE_NORMAL = 0; - FF_COMPLIANCE_INOFFICIAL = -1; ///< allow inofficial extensions - FF_COMPLIANCE_EXPERIMENTAL = -2; ///< allow non standarized experimental things - - FF_ER_CAREFUL = 1; - FF_ER_COMPLIANT = 2; - FF_ER_AGGRESSIVE = 3; - FF_ER_VERY_AGGRESSIVE = 4; - - FF_DCT_AUTO = 0; - FF_DCT_FASTINT = 1; - FF_DCT_INT = 2; - FF_DCT_MMX = 3; - FF_DCT_MLIB = 4; - FF_DCT_ALTIVEC = 5; - FF_DCT_FAAN = 6; - - FF_IDCT_AUTO = 0; - FF_IDCT_INT = 1; - FF_IDCT_SIMPLE = 2; - FF_IDCT_SIMPLEMMX = 3; - FF_IDCT_LIBMPEG2MMX = 4; - FF_IDCT_PS2 = 5; - FF_IDCT_MLIB = 6; - FF_IDCT_ARM = 7; - FF_IDCT_ALTIVEC = 8; - FF_IDCT_SH4 = 9; - FF_IDCT_SIMPLEARM = 10; - FF_IDCT_H264 = 11; - FF_IDCT_VP3 = 12; - FF_IDCT_IPP = 13; - FF_IDCT_XVIDMMX = 14; - FF_IDCT_CAVS = 15; - FF_IDCT_SIMPLEARMV5TE= 16; - FF_IDCT_SIMPLEARMV6 = 17; - FF_IDCT_SIMPLEVIS = 18; - FF_IDCT_WMV2 = 19; - FF_IDCT_FAAN = 20; - - FF_EC_GUESS_MVS = 1; - FF_EC_DEBLOCK = 2; - - FF_MM_FORCE = $80000000; (* force usage of selected flags (OR) *) - (* lower 16 bits - CPU features *) - FF_MM_MMX = $0001; ///< standard MMX - FF_MM_3DNOW = $0004; ///< AMD 3DNOW - FF_MM_MMXEXT = $0002; ///< SSE integer functions or AMD MMX ext - FF_MM_SSE = $0008; ///< SSE functions - FF_MM_SSE2 = $0010; ///< PIV SSE2 functions - FF_MM_3DNOWEXT = $0020; ///< AMD 3DNowExt - FF_MM_SSE3 = $0040; ///< Prescott SSE3 functions - FF_MM_SSSE3 = $0080; ///< Conroe SSSE3 functions - FF_MM_IWMMXT = $0100; ///< XScale IWMMXT - - FF_PRED_LEFT = 0; - FF_PRED_PLANE = 1; - FF_PRED_MEDIAN = 2; - - FF_DEBUG_PICT_INFO = 1; - FF_DEBUG_RC = 2; - FF_DEBUG_BITSTREAM = 4; - FF_DEBUG_MB_TYPE = 8; - FF_DEBUG_QP = 16; - FF_DEBUG_MV = 32; - FF_DEBUG_DCT_COEFF = $00000040; - FF_DEBUG_SKIP = $00000080; - FF_DEBUG_STARTCODE = $00000100; - FF_DEBUG_PTS = $00000200; - FF_DEBUG_ER = $00000400; - FF_DEBUG_MMCO = $00000800; - FF_DEBUG_BUGS = $00001000; - FF_DEBUG_VIS_QP = $00002000; - FF_DEBUG_VIS_MB_TYPE = $00004000; - FF_DEBUG_BUFFERS = $00008000; - - FF_DEBUG_VIS_MV_P_FOR = $00000001; //visualize forward predicted MVs of P frames - FF_DEBUG_VIS_MV_B_FOR = $00000002; //visualize forward predicted MVs of B frames - FF_DEBUG_VIS_MV_B_BACK = $00000004; //visualize backward predicted MVs of B frames - FF_CMP_SAD = 0; - FF_CMP_SSE = 1; - FF_CMP_SATD = 2; - FF_CMP_DCT = 3; - FF_CMP_PSNR = 4; - FF_CMP_BIT = 5; - FF_CMP_RD = 6; - FF_CMP_ZERO = 7; - FF_CMP_VSAD = 8; - FF_CMP_VSSE = 9; - FF_CMP_NSSE = 10; - FF_CMP_W53 = 11; - FF_CMP_W97 = 12; - FF_CMP_DCTMAX = 13; - FF_CMP_DCT264 = 14; - FF_CMP_CHROMA = 256; - - FF_DTG_AFD_SAME = 8; - FF_DTG_AFD_4_3 = 9; - FF_DTG_AFD_16_9 = 10; - FF_DTG_AFD_14_9 = 11; - FF_DTG_AFD_4_3_SP_14_9 = 13; - FF_DTG_AFD_16_9_SP_14_9 = 14; - FF_DTG_AFD_SP_4_3 = 15; - - FF_DEFAULT_QUANT_BIAS = 999999; - - FF_LAMBDA_SHIFT = 7; - FF_LAMBDA_SCALE = (1 shl FF_LAMBDA_SHIFT); - FF_QP2LAMBDA = 118; ///< factor to convert from H.263 QP to lambda - FF_LAMBDA_MAX = (256 * 128 - 1); - - FF_QUALITY_SCALE = FF_LAMBDA_SCALE; //FIXME maybe remove - - FF_CODER_TYPE_VLC = 0; - FF_CODER_TYPE_AC = 1; - FF_CODER_TYPE_RAW = 2; - FF_CODER_TYPE_RLE = 3; - FF_CODER_TYPE_DEFLATE = 4; - - SLICE_FLAG_CODED_ORDER = $0001; ///< draw_horiz_band() is called in coded order instead of display - SLICE_FLAG_ALLOW_FIELD = $0002; ///< allow draw_horiz_band() with field slices (MPEG2 field pics) - SLICE_FLAG_ALLOW_PLANE = $0004; ///< allow draw_horiz_band() with 1 component at a time (SVQ1) - - FF_MB_DECISION_SIMPLE = 0; ///< uses mb_cmp - FF_MB_DECISION_BITS = 1; ///< chooses the one which needs the fewest bits - FF_MB_DECISION_RD = 2; ///< rate distortion - - FF_AA_AUTO = 0; - FF_AA_FASTINT = 1; //not implemented yet - FF_AA_INT = 2; - FF_AA_FLOAT = 3; - - FF_PROFILE_UNKNOWN = -99; - FF_PROFILE_AAC_MAIN = 0; - FF_PROFILE_AAC_LOW = 1; - FF_PROFILE_AAC_SSR = 2; - FF_PROFILE_AAC_LTP = 3; - - FF_LEVEL_UNKNOWN = -99; - - X264_PART_I4X4 = $001; (* Analyse i4x4 *) - X264_PART_I8X8 = $002; (* Analyse i8x8 (requires 8x8 transform) *) - X264_PART_P8X8 = $010; (* Analyse p16x8, p8x16 and p8x8 *) - X264_PART_P4X4 = $020; (* Analyse p8x4, p4x8, p4x4 *) - X264_PART_B8X8 = $100; (* Analyse b16x8, b8x16 and b8x8 *) - - FF_COMPRESSION_DEFAULT = -1; + {$IF LIBAVCODEC_VERSION >= 52002000} // >= 52.2.0 + (** + * Audio channel layout. + * - encoding: set by user. + * - decoding: set by libavcodec. + *) + channel_layout: cint64; -const - AVPALETTE_SIZE = 1024; - AVPALETTE_COUNT = 256; + (** + * Request decoder to use this channel layout if it can (0 for default) + * - encoding: unused + * - decoding: Set by user. + *) + request_channel_layout: cint64; + {$IFEND} -type -(** - * AVPaletteControl - * This structure defines a method for communicating palette changes - * between and demuxer and a decoder. - * - * @deprecated Use AVPacket to send palette changes instead. - * This is totally broken. - *) - PAVPaletteControl = ^TAVPaletteControl; - TAVPaletteControl = record - (* demuxer sets this to 1 to indicate the palette has changed; - * decoder resets to 0 *) - palette_changed: cint; + {$IF LIBAVCODEC_VERSION >= 52004000} // >= 52.4.0 + (** + * Ratecontrol attempt to use, at maximum, <value> of what can be used without an underflow. + * - encoding: Set by user. + * - decoding: unused. + *) + rc_max_available_vbv_use: cfloat; - (* 4-byte ARGB palette entries, stored in native byte order; note that - * the individual palette components should be on a 8-bit scale; if - * the palette data comes from a IBM VGA native format, the component - * data is probably 6 bits in size and needs to be scaled *) - palette: array [0..AVPALETTE_COUNT - 1] of cuint; - end; {deprecated;} + (** + * Ratecontrol attempt to use, at least, <value> times the amount needed to prevent a vbv overflow. + * - encoding: Set by user. + * - decoding: unused. + *) + rc_min_vbv_overflow_use: cfloat; + {$IFEND} + {$IF LIBAVCODEC_VERSION >= 52018000} // >= 52.18.0 + (** + * Hardware accelerator in use + * - encoding: unused. + * - decoding: Set by libavcodec + *) + hwaccel: PAVHWAccel; + {$IFEND} + {$IF LIBAVCODEC_VERSION >= 52020000} // >= 52.20.0 + (** + * For some codecs, the time base is closer to the field rate than the frame rate. + * Most notably, H.264 and MPEG-2 specify time_base as half of frame duration + * if no telecine is used ... + * + * Set to time_base ticks per frame. Default 1, e.g., H.264/MPEG-2 set it to 2. + *) + ticks_per_frame: cint; + {$IFEND} + {$IF LIBAVCODEC_VERSION >= 52021000} // >= 52.21.0 + (** + * Hardware accelerator context. + * For some hardware accelerators, a global context needs to be + * provided by the user. In that case, this holds display-dependent + * data FFmpeg cannot instantiate itself. Please refer to the + * FFmpeg HW accelerator documentation to know how to fill this + * is. e.g. for VA API, this is a struct vaapi_context. + * - encoding: unused + * - decoding: Set by user + *) + hwaccel_context: pointer; + {$IFEND} + {$IF LIBAVCODEC_VERSION >= 52028000} // >= 52.28.0 + (** + * Chromaticity coordinates of the source primaries. + * - encoding: Set by user + * - decoding: Set by libavcodec + *) + color_primaries: TAVColorPrimaries; -type - PAVClass = ^TAVClass; {const} - PAVCodecContext = ^TAVCodecContext; + (** + * Color Transfer Characteristic. + * - encoding: Set by user + * - decoding: Set by libavcodec + *) + color_trc: TAVColorTransferCharacteristic; - PAVCodec = ^TAVCodec; + (** + * YUV colorspace type. + * - encoding: Set by user + * - decoding: Set by libavcodec + *) + colorspace: TAVColorSpace; - // int[4] - PQuadIntArray = ^TQuadIntArray; - TQuadIntArray = array[0..3] of cint; - // int (*func)(struct AVCodecContext *c2, void *arg) - TExecuteFunc = function(c2: PAVCodecContext; arg: Pointer): cint; cdecl; + (** + * MPEG vs JPEG YUV range. + * - encoding: Set by user + * - decoding: Set by libavcodec + *) + color_range: TAVColorRange; - TAVClass = record {12} - class_name: pchar; - (* actually passing a pointer to an AVCodecContext - or AVFormatContext, which begin with an AVClass. - Needed because av_log is in libavcodec and has no visibility - of AVIn/OutputFormat *) - item_name: function (): pchar; cdecl; - option: PAVOption; + (** + * This defines the location of chroma samples. + * - encoding: Set by user + * - decoding: Set by libavcodec + *) + chroma_sample_location: TAVChromaLocation; + {$IFEND} end; (** @@ -1191,6 +1570,13 @@ type * decoder to draw a horizontal band. It improves cache usage. Not * all codecs can do that. You must check the codec capabilities * beforehand. + * The function is also used by hardware acceleration APIs. + * It is called at least once during frame decoding to pass + * the data needed for hardware render. + * In that mode instead of pixel data, AVFrame points to + * a structure specific to the acceleration API. The application + * reads the structure and can change some fields to indicate progress + * or mark state. * - encoding: unused * - decoding: Set by user. * @param height the height of the slice @@ -1211,7 +1597,7 @@ type * - encoding: Set by user. * - decoding: Set by libavcodec. *) - sample_fmt: TSampleFormat; ///< sample format, currenly unused + sample_fmt: TSampleFormat; ///< sample format (* The following data should not be initialized. *) (** @@ -1219,7 +1605,9 @@ type *) frame_size: cint; frame_number: cint; ///< audio or video frame number +{$IF LIBAVCODEC_MAX_VERSION_MAJOR < 53} real_pict_num: cint; ///< returns the real picture number of previous encoded frame +{$IFEND} (** * Number of frames the decoded output will be delayed relative to @@ -1334,7 +1722,7 @@ type *) opaque: pointer; - codec_name: array [0..31] of char; + codec_name: array [0..31] of AnsiChar; codec_type: TCodecType; (* see CODEC_TYPE_xxx *) codec_id: TCodecID; (* see CODEC_ID_xxx *) @@ -1408,6 +1796,9 @@ type * If pic.reference is set then the frame will be read later by libavcodec. * avcodec_align_dimensions() should be used to find the required width and * height, as they normally need to be rounded up to the next multiple of 16. + * if CODEC_CAP_DR1 is not set then get_buffer() must call + * avcodec_default_get_buffer() instead of providing buffers allocated by + * some other means. * - encoding: unused * - decoding: Set by libavcodec., user can override. *) @@ -1423,7 +1814,8 @@ type release_buffer: procedure (c: PAVCodecContext; pic: PAVFrame); cdecl; (** - * If 1 the stream has a 1 frame delay during decoding. + * Size of the frame reordering buffer in the decoder. + * For MPEG-2 it is 1 IPB or 0 low delay IP. * - encoding: Set by libavcodec. * - decoding: Set by libavcodec. *) @@ -1451,7 +1843,7 @@ type * - encoding: Set by libavcodec. * - decoding: unused *) - stats_out: pchar; + stats_out: PByteArray; (** * pass2 encoding statistics input buffer @@ -1459,7 +1851,7 @@ type * - encoding: Allocated/set/freed by user. * - decoding: unused *) - stats_in: pchar; + stats_in: PByteArray; (** * ratecontrol qmin qmax limiting method @@ -1485,7 +1877,7 @@ type * - encoding: Set by user * - decoding: unused *) - rc_eq: {const} pchar; + rc_eq: {const} PByteArray; (** * maximum bitrate @@ -1886,7 +2278,7 @@ type * - encoding: unused * - decoding: Set by user, will be converted to uppercase by libavcodec during init. *) - stream_codec_tag: array [0..3] of char; //cuint; + stream_codec_tag: array [0..3] of AnsiChar; //cuint; (** * scene change detection threshold @@ -1930,6 +2322,9 @@ type * libavcodec will pass previous buffer in pic, function should return * same buffer or new buffer with old frame "painted" into it. * If pic.data[0] == NULL must behave like get_buffer(). + * if CODEC_CAP_DR1 is not set then reget_buffer() must call + * avcodec_default_reget_buffer() instead of providing buffers allocated by + * some other means. * - encoding: unused * - decoding: Set by libavcodec., user can override *) @@ -1994,7 +2389,11 @@ type * - encoding: Set by libavcodec, user can override. * - decoding: Set by libavcodec, user can override. *) + {$IF LIBAVCODEC_VERSION < 52004000} // < 52.4.0 execute: function (c: PAVCodecContext; func: TExecuteFunc; arg: PPointer; ret: PCint; count: cint): cint; cdecl; + {$ELSE} + execute: function (c: PAVCodecContext; func: TExecuteFunc; arg: Pointer; ret: PCint; count: cint; size: cint): cint; cdecl; + {$IFEND} (** * thread opaque @@ -2197,7 +2596,7 @@ type (** * number of reference frames * - encoding: Set by user. - * - decoding: unused + * - decoding: Set by lavc. *) refs: cint; @@ -2349,13 +2748,16 @@ type {$IFEND} {$IF LIBAVCODEC_VERSION >= 51042000} // 51.42.0 + {$IF LIBAVCODEC_MAX_VERSION_MAJOR < 53} (** * Decoder should decode to this many channels if it can (0 for default) * - encoding: unused * - decoding: Set by user. + * @deprecated Deprecated in favor of request_channel_layout. *) request_channels: cint; {$IFEND} + {$IFEND} {$IF LIBAVCODEC_VERSION > 51049000} // > 51.49.0 (** @@ -2376,21 +2778,66 @@ type *) reordered_opaque: cint64; {$IFEND} + + {$IF LIBAVCODEC_VERSION >= 52028000} // 52.28.0 + (** + * This defines the location of chroma samples. + * - encoding: Set by user + * - decoding: Set by libavcodec + *) + chroma_sample_location: TAVChromaLocation; + {$IFEND} + {$IF LIBAVCODEC_VERSION >= 52037000} // >= 52.37.0 + (** + * The codec may call this to execute several independent things. + * It will return only after finishing all tasks. + * The user may replace this with some multithreaded implementation, + * the default implementation will execute the parts serially. + * Also see avcodec_thread_init and e.g. the --enable-pthread configure option. + * @param c context passed also to func + * @param count the number of things to execute + * @param arg2 argument passed unchanged to func + * @param ret return values of executed functions, must have space for "count" values. May be NULL. + * @param func function that will be called count times, with jobnr from 0 to count-1. + * threadnr will be in the range 0 to c->thread_count-1 < MAX_THREADS and so that no + * two instances of func executing at the same time will have the same threadnr. + * @return always 0 currently, but code should handle a future improvement where when any call to func + * returns < 0 no further calls to func may be done and < 0 is returned. + * - encoding: Set by libavcodec, user can override. + * - decoding: Set by libavcodec, user can override. + *) + execute2: function (c: PAVCodecContext; func: function (c2: PAVCodecContext; arg: Pointer; jobnr: cint; threadnr: cint): cint; cdecl; arg2: Pointer; ret: Pcint; count: cint): cint; cdecl; + {$IFEND} + {$IF LIBAVCODEC_VERSION >= 52042000} // >= 52.42.0 + (** + * explicit P-frame weighted prediction analysis method + * 0: off + * 1: fast blind weighting (one reference duplicate with -1 offset) + * 2: smart weighting (full fade detection analysis) + * - encoding: Set by user. + * - decoding: unused + *) + weighted_p_pred: cint; + {$IFEND} end; (** * AVCodec. *) TAVCodec = record - name: pchar; + name: PAnsiChar; type_: TCodecType; id: TCodecID; priv_data_size: cint; init: function (avctx: PAVCodecContext): cint; cdecl; (* typo corretion by the Creative CAT *) - encode: function (avctx: PAVCodecContext; buf: pchar; buf_size: cint; data: pointer): cint; cdecl; + encode: function (avctx: PAVCodecContext; buf: PByteArray; buf_size: cint; data: pointer): cint; cdecl; close: function (avctx: PAVCodecContext): cint; cdecl; decode: function (avctx: PAVCodecContext; outdata: pointer; var outdata_size: cint; - buf: {const} pchar; buf_size: cint): cint; cdecl; + {$IF LIBAVCODEC_VERSION < 52025000} // 52.25.0 + buf: {const} PByteArray; buf_size: cint): cint; cdecl; + {$ELSE} + avpkt: PAVPacket): cint; cdecl; + {$IFEND} (** * Codec capabilities. * see CODEC_CAP_* @@ -2406,10 +2853,10 @@ type pix_fmts: {const} PAVPixelFormat; ///< array of supported pixel formats, or NULL if unknown, array is terminated by -1 {$IF LIBAVCODEC_VERSION >= 51055000} // 51.55.0 (** - * Descriptive name for the codec, meant to be more human readable than \p name. - * You \e should use the NULL_IF_CONFIG_SMALL() macro to define it. + * Descriptive name for the codec, meant to be more human readable than name. + * You should use the NULL_IF_CONFIG_SMALL() macro to define it. *) - long_name: {const} PChar; + long_name: {const} PAnsiChar; {$IFEND} {$IF LIBAVCODEC_VERSION >= 51056000} // 51.56.0 supported_samplerates: {const} PCint; ///< array of supported audio samplerates, or NULL if unknown, array is terminated by 0 @@ -2417,7 +2864,109 @@ type {$IF LIBAVCODEC_VERSION >= 51062000} // 51.62.0 sample_fmts: {const} PSampleFormatArray; ///< array of supported sample formats, or NULL if unknown, array is terminated by -1 {$IFEND} + {$IF LIBAVCODEC_VERSION >= 52002000} // 52.2.0 + channel_layouts: {const} PCint64; ///< array of support channel layouts, or NULL if unknown. array is terminated by 0 + {$IFEND} + end; + +{$IF LIBAVCODEC_VERSION >= 52018000} // 52.18.0 +(** + * AVHWAccel. + *) + TAVHWAccel = record + (** + * Name of the hardware accelerated codec. + * The name is globally unique among encoders and among decoders (but an + * encoder and a decoder can share the same name). + *) + name: PAnsiChar; + + (** + * Type of codec implemented by the hardware accelerator. + * + * See CODEC_TYPE_xxx + *) + type_: TCodecType; + + (** + * Codec implemented by the hardware accelerator. + * + * See CODEC_ID_xxx + *) + id: TCodecID; + + (** + * Supported pixel format. + * + * Only hardware accelerated formats are supported here. + *) + pix_fmt: {const} PAVPixelFormat; + + (** + * Hardware accelerated codec capabilities. + * see FF_HWACCEL_CODEC_CAP_* + *) + capabilities: cint; + + next: PAVCodec; + + (** + * Called at the beginning of each frame or field picture. + * + * Meaningful frame information (codec specific) is guaranteed to + * be parsed at this point. This function is mandatory. + * + * Note that buf can be NULL along with buf_size set to 0. + * Otherwise, this means the whole frame is available at this point. + * + * @param avctx the codec context + * @param buf the frame data buffer base + * @param buf_size the size of the frame in bytes + * @return zero if successful, a negative value otherwise + *) + start_frame: function (avctx: PAVCodecContext; + buf: PByteArray; + buf_size: cint): cint; cdecl; + + (** + * Callback for each slice. + * + * Meaningful slice information (codec specific) is guaranteed to + * be parsed at this point. This function is mandatory. + * + * @param avctx the codec context + * @param buf the slice data buffer base + * @param buf_size the size of the slice in bytes + * @return zero if successful, a negative value otherwise + *) + decode_slice: function (avctx: PAVCodecContext; + buf: PByteArray; + buf_size: cint): cint; cdecl; + + (** + * Called at the end of each frame or field picture. + * + * The whole picture is parsed at this point and can now be sent + * to the hardware accelerator. This function is mandatory. + * + * @param avctx the codec context + * @return zero if successful, a negative value otherwise + *) + end_frame: function (avctx: PAVCodecContext): cint; cdecl; + +{$IF LIBAVCODEC_VERSION >= 52021000} // >= 52.21.0 + (** + * Size of HW accelerator private data. + * + * Private data is allocated with av_mallocz() before + * AVCodecContext.get_buffer() and deallocated after + * AVCodecContext.release_buffer(). + *) + priv_data_size: cint; +{$IFEND} + end; +{$IFEND} (** * four components are given, that's all. @@ -2425,35 +2974,149 @@ type *) PAVPicture = ^TAVPicture; TAVPicture = record - data: array [0..3] of pchar; + data: array [0..3] of PByteArray; linesize: array [0..3] of cint; ///< number of bytes per line end; type + TAVSubtitleType = ( + SUBTITLE_NONE, + + SUBTITLE_BITMAP, ///< A bitmap, pict will be set + + (** + * Plain text, the text field must be set by the decoder and is + * authoritative. ass and pict fields may contain approximations. + *) + SUBTITLE_TEXT, + + (** + * Formatted text, the ass field must be set by the decoder and is + * authoritative. pict and text fields may contain approximations. + *) + SUBTITLE_ASS + ); + +type + PPAVSubtitleRect = ^PAVSubtitleRect; PAVSubtitleRect = ^TAVSubtitleRect; + {$IF LIBAVCODEC_VERSION < 52010000} // < 52.10.0 TAVSubtitleRect = record - x: word; - y: word; - w: word; - h: word; - nb_colors: word; + x: cuint16; + y: cuint16; + w: cuint16; + h: cuint16; + nb_colors: cuint16; linesize: cint; - rgba_palette: PCuint; - bitmap: pchar; + rgba_palette: PCuint32; + bitmap: PCuint8; + end; + {$ELSE} + TAVSubtitleRect = record + x: cint; ///< top left corner of pict, undefined when pict is not set + y: cint; ///< top left corner of pict, undefined when pict is not set + w: cint; ///< width of pict, undefined when pict is not set + h: cint; ///< height of pict, undefined when pict is not set + nb_colors: cint; ///< number of colors in pict, undefined when pict is not set + + (** + * data+linesize for the bitmap of this subtitle. + * can be set for text/ass as well once they where rendered + *) + pict: TAVPicture; + type_: TAVSubtitleType; + + text: PAnsiChar; ///< 0 terminated plain UTF-8 text + + (** + * 0 terminated ASS/SSA compatible event line. + * The pressentation of this is unaffected by the other values in this + * struct. + *) + ass: PByteArray; end; + {$IFEND} + PPAVSubtitle = ^PAVSubtitle; PAVSubtitle = ^TAVSubtitle; - TAVSubtitle = record {20} - format: word; (* 0 = graphics *) - start_display_time: cuint; (* relative to packet pts, in ms *) - end_display_time: cuint; (* relative to packet pts, in ms *) + TAVSubtitle = record + format: cuint16; (* 0 = graphics *) + start_display_time: cuint32; (* relative to packet pts, in ms *) + end_display_time: cuint32; (* relative to packet pts, in ms *) num_rects: cuint; + {$IF LIBAVCODEC_VERSION < 52010000} // < 52.10.0 rects: PAVSubtitleRect; + {$ELSE} + rects: PPAVSubtitleRect; + {$IFEND} + {$IF LIBAVCODEC_VERSION >= 52032000} // >= 52.32.0 + pts: cint64; ///< Same as packet pts, in AV_TIME_BASE + {$IFEND} end; +{$IF LIBAVCODEC_VERSION >= 52025000} // 52.25.0 +{ packet functions } -(* resample.c *) +(** + * @deprecated use NULL instead + *) +procedure av_destruct_packet_nofree(pkt: PAVPacket); + cdecl; external av__codec; + +(* + * Default packet destructor. + *) +procedure av_destruct_packet(pkt: PAVPacket); + cdecl; external av__codec; +(* + * Initialize optional fields of a packet with default values. + * + * @param pkt packet + *) +procedure av_init_packet(var pkt: TAVPacket); + cdecl; external av__codec; + +(* + * Allocate the payload of a packet and initialize its fields with + * default values. + * + * @param pkt packet + * @param size wanted payload size + * @return 0 if OK, AVERROR_xxx otherwise + *) +function av_new_packet(pkt: PAVPacket; size: cint): cint; + cdecl; external av__codec; + +(* + * Reduce packet size, correctly zeroing padding + * + * @param pkt packet + * @param size new size + *) +procedure av_shrink_packet(pkt: PAVPacket; size: cint); + cdecl; external av__codec; + +(* + * @warning This is a hack - the packet memory allocation stuff is broken. The + * packet is allocated if it was not really allocated. + *) +function av_dup_packet(pkt: PAVPacket): cint; + cdecl; external av__codec; + +(* + * Free a packet. + * + * @param pkt packet to free + *) +procedure av_free_packet(pkt: PAVPacket); +{$IF LIBAVCODEC_VERSION >= 52028000} // 52.28.0 + cdecl; external av__codec; +{$IFEND} +{$IFEND} + +(* resample.c *) +type PReSampleContext = pointer; PAVResampleContext = pointer; PImgReSampleContext = pointer; @@ -2468,15 +3131,44 @@ function audio_resample (s: PReSampleContext; output: PSmallint; input: PSmallin procedure audio_resample_close (s: PReSampleContext); cdecl; external av__codec; - +(** + * Initializes an audio resampler. + * Note, if either rate is not an integer then simply scale both rates up so they are. + * @param filter_length length of each FIR filter in the filterbank relative to the cutoff freq + * @param log2_phase_count log2 of the number of entries in the polyphase filterbank + * @param linear If 1 then the used FIR filter will be linearly interpolated + between the 2 closest, if 0 the closest will be used + * @param cutoff cutoff frequency, 1.0 corresponds to half the output sampling rate + *) function av_resample_init (out_rate: cint; in_rate: cint; filter_length: cint; log2_phase_count: cint; linear: cint; cutoff: cdouble): PAVResampleContext; cdecl; external av__codec; +(** + * resamples. + * @param src an array of unconsumed samples + * @param consumed the number of samples of src which have been consumed are returned here + * @param src_size the number of unconsumed samples available + * @param dst_size the amount of space in samples available in dst + * @param update_ctx If this is 0 then the context will not be modified, that way several channels can be resampled with the same context. + * @return the number of samples written in dst or -1 if an error occurred + *) function av_resample (c: PAVResampleContext; dst: PSmallint; src: PSmallint; var consumed: cint; src_size: cint; dst_size: cint; update_ctx: cint): cint; cdecl; external av__codec; +(** + * Compensates samplerate/timestamp drift. The compensation is done by changing + * the resampler parameters, so no audible clicks or similar distortions occur + * @param compensation_distance distance in output samples over which the compensation should be performed + * @param sample_delta number of output samples which should be output less + * + * example: av_resample_compensate(c, 10, 500) + * here instead of 510 samples only 500 samples would be output + * + * note, due to rounding the actual compensation might be slightly different, + * especially if the compensation_distance is large and the in_rate used during init is small + *) procedure av_resample_compensate (c: PAVResampleContext; sample_delta: cint; compensation_distance: cint); cdecl; external av__codec; @@ -2484,7 +3176,6 @@ procedure av_resample_compensate (c: PAVResampleContext; sample_delta: cint; procedure av_resample_close (c: PAVResampleContext); cdecl; external av__codec; - {$IF LIBAVCODEC_VERSION < 52000000} // 52.0.0 (* YUV420 format is assumed ! *) @@ -2517,7 +3208,6 @@ procedure img_resample (s: PImgReSampleContext; output: PAVPicture; input: {cons *) procedure img_resample_close (s: PImgReSampleContext); cdecl; external av__codec; deprecated; - {$IFEND} (** @@ -2549,6 +3239,7 @@ procedure avpicture_free (picture: PAVPicture); * If a planar format is specified, several pointers will be set pointing to * the different picture planes and the line sizes of the different planes * will be stored in the lines_sizes array. + * Call with ptr == NULL to get the required size for the ptr buffer. * * @param picture AVPicture whose fields are to be filled in * @param ptr Buffer which will contain or contains the actual image data @@ -2563,17 +3254,20 @@ function avpicture_fill (picture: PAVPicture; ptr: pointer; function avpicture_layout (src: {const} PAVPicture; pix_fmt: TAVPixelFormat; width: cint; height: cint; - dest: pchar; dest_size: cint): cint; + dest: PByteArray; dest_size: cint): cint; cdecl; external av__codec; (** * Calculate the size in bytes that a picture of the given width and height * would occupy if stored in the given picture format. + * Note that this returns the size of a compact representation as generated + * by avpicture_layout, which can be smaller than the size required for e.g. + * avpicture_fill. * * @param pix_fmt the given picture format * @param width the width of the image * @param height the height of the image - * @return Image data size in bytes + * @return Image data size in bytes or -1 on error (e.g. too large dimensions). *) function avpicture_get_size (pix_fmt: TAVPixelFormat; width: cint; height: cint): cint; cdecl; external av__codec; @@ -2581,13 +3275,35 @@ function avpicture_get_size (pix_fmt: TAVPixelFormat; width: cint; height: cint) procedure avcodec_get_chroma_sub_sample (pix_fmt: TAVPixelFormat; var h_shift: cint; var v_shift: cint); cdecl; external av__codec; -function avcodec_get_pix_fmt_name(pix_fmt: TAVPixelFormat): pchar; +(** + * Returns the pixel format corresponding to the name \p name. + * + * If there is no pixel format with name \p name, then looks for a + * pixel format with the name corresponding to the native endian + * format of \p name. + * For example in a little-endian system, first looks for "gray16", + * then for "gray16le". + * + * Finally if no pixel format has been found, returns \c PIX_FMT_NONE. + *) +function avcodec_get_pix_fmt_name(pix_fmt: TAVPixelFormat): PAnsiChar; cdecl; external av__codec; procedure avcodec_set_dimensions(s: PAVCodecContext; width: cint; height: cint); cdecl; external av__codec; -function avcodec_get_pix_fmt(name: {const} pchar): TAVPixelFormat; +(** + * Returns the pixel format corresponding to the name name. + * + * If there is no pixel format with name name, then looks for a + * pixel format with the name corresponding to the native endian + * format of name. + * For example in a little-endian system, first looks for "gray16", + * then for "gray16le". + * + * Finally if no pixel format has been found, returns PIX_FMT_NONE. + *) +function avcodec_get_pix_fmt(name: {const} PAnsiChar): TAVPixelFormat; cdecl; external av__codec; function avcodec_pix_fmt_to_codec_tag(p: TAVPixelFormat): cuint; @@ -2630,7 +3346,7 @@ function avcodec_get_pix_fmt_loss (dst_pix_fmt: TAVPixelFormat; src_pix_fmt: TAV * some formats to other formats. avcodec_find_best_pix_fmt() searches which of * the given pixel formats should be used to suffer the least amount of loss. * The pixel formats from which it chooses one, are determined by the - * \p pix_fmt_mask parameter. + * pix_fmt_mask parameter. * * @code * src_pix_fmt = PIX_FMT_YUV420P; @@ -2648,10 +3364,14 @@ function avcodec_get_pix_fmt_loss (dst_pix_fmt: TAVPixelFormat; src_pix_fmt: TAV function avcodec_find_best_pix_fmt(pix_fmt_mask: cint64; src_pix_fmt: TAVPixelFormat; has_alpha: cint; loss_ptr: PCint): cint; cdecl; external av__codec; -{$ELSE} +{$ELSEIF LIBAVCODEC_VERSION < 52022001} function avcodec_find_best_pix_fmt(pix_fmt_mask: cint; src_pix_fmt: TAVPixelFormat; has_alpha: cint; loss_ptr: PCint): cint; cdecl; external av__codec; +{$ELSE} +function avcodec_find_best_pix_fmt(pix_fmt_mask: cint; src_pix_fmt: TAVPixelFormat; + has_alpha: cint; loss_ptr: PCint): TAVPixelFormat; + cdecl; external av__codec; {$IFEND} {$IF LIBAVCODEC_VERSION >= 51041000} // 51.41.0 @@ -2665,9 +3385,14 @@ function avcodec_find_best_pix_fmt(pix_fmt_mask: cint; src_pix_fmt: TAVPixelForm * a negative value to print the corresponding header. * Meaningful values for obtaining a pixel format info vary from 0 to PIX_FMT_NB -1. *) -procedure avcodec_pix_fmt_string (buf: PChar; buf_size: cint; pix_fmt: cint); +{$IF LIBAVCODEC_VERSION < 52022001} // 52.22.1 +procedure avcodec_pix_fmt_string (buf: PAnsiChar; buf_size: cint; pix_fmt: cint); + cdecl; external av__codec; +{$ELSE} +procedure avcodec_pix_fmt_string (buf: PAnsiChar; buf_size: cint; pix_fmt: TAVPixelFormat); cdecl; external av__codec; {$IFEND} +{$IFEND} const FF_ALPHA_TRANSP = $0001; {* image has some totally transparent pixels *} @@ -2679,7 +3404,8 @@ const *) function img_get_alpha_info (src: {const} PAVPicture; pix_fmt: TAVPixelFormat; - width: cint; height: cint): cint; + width: cint; + height: cint): cint; cdecl; external av__codec; {$IF LIBAVCODEC_VERSION < 52000000} // 52.0.0 @@ -2695,8 +3421,11 @@ function img_convert (dst: PAVPicture; dst_pix_fmt: TAVPixelFormat; (* deinterlace a picture *) (* deinterlace - if not supported return -1 *) -function avpicture_deinterlace (dst: PAVPicture; src: {const} PAVPicture; - pix_fmt: TAVPixelFormat; width: cint; height: cint): cint; +function avpicture_deinterlace (dst: PAVPicture; + src: {const} PAVPicture; + pix_fmt: TAVPixelFormat; + width: cint; + height: cint): cint; cdecl; external av__codec; {* external high level API *} @@ -2709,6 +3438,11 @@ var {$IFEND} {$IF LIBAVCODEC_VERSION >= 51049000} // 51.49.0 +(** + * If c is NULL, returns the first registered codec, + * if c is non-NULL, returns the next registered codec after c, + * or NULL if c is the last one. + *) function av_codec_next(c: PAVCodec): PAVCodec; cdecl; external av__codec; {$IFEND} @@ -2725,18 +3459,44 @@ function avcodec_build(): cuint; cdecl; external av__codec; deprecated; {$IFEND} +{$IF LIBAVCODEC_VERSION >= 52041000} // 52.41.0 +(** + * Returns the libavcodec build-time configuration. + *) +function avcodec_configuration(): PAnsiChar; + cdecl; external av__codec; + +(** + * Returns the libavcodec license. + *) +function avcodec_license(): PAnsiChar; + cdecl; external av__codec; +{$IFEND} + (** * Initializes libavcodec. * - * @warning This function \e must be called before any other libavcodec + * @warning This function must be called before any other libavcodec * function. *) procedure avcodec_init(); cdecl; external av__codec; -procedure register_avcodec(format: PAVCodec); +(** + * Register the codec codec and initialize libavcodec. + * + * @see avcodec_init() + *) +{$IF LIBAVCODEC_VERSION >= 52014000} // 52.14.0 +procedure avcodec_register(codec: PAVCodec); cdecl; external av__codec; - +// Deprecated in favor of avcodec_register. +procedure register_avcodec(codec: PAVCodec); + cdecl; external av__codec; deprecated; +{$ELSEIF LIBAVCODEC_VERSION_MAJOR < 53} +procedure register_avcodec(codec: PAVCodec); + cdecl; external av__codec; +{$IFEND} (** * Finds a registered encoder with a matching codec ID. * @@ -2752,7 +3512,7 @@ function avcodec_find_encoder(id: TCodecID): PAVCodec; * @param name name of the requested encoder * @return An encoder if one was found, NULL otherwise. *) -function avcodec_find_encoder_by_name(name: pchar): PAVCodec; +function avcodec_find_encoder_by_name(name: PAnsiChar): PAVCodec; cdecl; external av__codec; (** @@ -2770,9 +3530,9 @@ function avcodec_find_decoder(id: TCodecID): PAVCodec; * @param name name of the requested decoder * @return A decoder if one was found, NULL otherwise. *) -function avcodec_find_decoder_by_name(name: pchar): PAVCodec; +function avcodec_find_decoder_by_name(name: PAnsiChar): PAVCodec; cdecl; external av__codec; -procedure avcodec_string(buf: pchar; buf_size: cint; enc: PAVCodecContext; encode: cint); +procedure avcodec_string(buf: PAnsiChar; buf_size: cint; enc: PAVCodecContext; encode: cint); cdecl; external av__codec; (** @@ -2851,10 +3611,27 @@ function avcodec_thread_init(s: PAVCodecContext; thread_count: cint): cint; cdecl; external av__codec; procedure avcodec_thread_free(s: PAVCodecContext); cdecl; external av__codec; + + +{$IF LIBAVCODEC_VERSION < 52004000} // < 52.4.0 function avcodec_thread_execute(s: PAVCodecContext; func: TExecuteFunc; arg: PPointer; var ret: cint; count: cint): cint; cdecl; external av__codec; +{$ELSE} +function avcodec_thread_execute(s: PAVCodecContext; func: TExecuteFunc; arg: Pointer; var ret: cint; count: cint; size: cint): cint; + cdecl; external av__codec; +{$IFEND} + +{$IF LIBAVCODEC_VERSION < 52004000} // < 52.4.0 function avcodec_default_execute(s: PAVCodecContext; func: TExecuteFunc; arg: PPointer; var ret: cint; count: cint): cint; cdecl; external av__codec; +{$ELSE} +function avcodec_default_execute(s: PAVCodecContext; func: TExecuteFunc; arg: Pointer; var ret: cint; count: cint; size: cint): cint; + cdecl; external av__codec; +{$IFEND} +{$IF LIBAVCODEC_VERSION >= 52037000} // >= 52.37.0 +function avcodec_default_execute2(s: PAVCodecContext; func: TExecuteFunc; arg: Pointer; var ret: cint; count: cint): cint; + cdecl; external av__codec; +{$IFEND} //FIXME func typedef (** @@ -2889,122 +3666,191 @@ function avcodec_open(avctx: PAVCodecContext; codec: PAVCodec): cint; {$IF LIBAVCODEC_VERSION < 52000000} // < 52.0.0 (** - * @deprecated Use avcodec_decode_audio2() instead. + * @deprecated Use avcodec_decode_audio2 instead. *) function avcodec_decode_audio(avctx: PAVCodecContext; samples: PSmallint; var frame_size_ptr: cint; - buf: {const} pchar; buf_size: cint): cint; - cdecl; external av__codec; + buf: {const} PByteArray; buf_size: cint): cint; + cdecl; external av__codec; {deprecated;} {$IFEND} +{$IF LIBAVCODEC_MAX_VERSION_MAJOR < 53} {$IF LIBAVCODEC_VERSION >= 51030000} // 51.30.0 (** - * Decodes an audio frame from \p buf into \p samples. - * The avcodec_decode_audio2() function decodes an audio frame from the input - * buffer \p buf of size \p buf_size. To decode it, it makes use of the - * audio codec which was coupled with \p avctx using avcodec_open(). The - * resulting decoded frame is stored in output buffer \p samples. If no frame - * could be decompressed, \p frame_size_ptr is zero. Otherwise, it is the - * decompressed frame size in \e bytes. + * Decodes an audio frame from buf into samples. + * Wrapper function which calls avcodec_decode_audio3. * - * @warning You \e must set \p frame_size_ptr to the allocated size of the - * output buffer before calling avcodec_decode_audio2(). + * @deprecated Use avcodec_decode_audio3 instead. + * @param avctx the codec context + * @param[out] samples the output buffer, sample type in avctx->sample_fmt + * @param[in,out] frame_size_ptr the output buffer size in bytes + * @param[in] buf the input buffer + * @param[in] buf_size the input buffer size in bytes + * @return On error a negative value is returned, otherwise the number of bytes + * used or zero if no frame could be decompressed. + *) +function avcodec_decode_audio2(avctx: PAVCodecContext; samples: PSmallint; + var frame_size_ptr: cint; + buf: {const} PByteArray; buf_size: cint): cint; + cdecl; external av__codec; {deprecated;} +{$IFEND} +{$IFEND} + +{$IF LIBAVCODEC_VERSION >= 52025000} // 52.25.0 +(** + * Decodes the audio frame of size avpkt->size from avpkt->data into samples. + * Some decoders may support multiple frames in a single AVPacket, such + * decoders would then just decode the first frame. In this case, + * avcodec_decode_audio3 has to be called again with an AVPacket that contains + * the remaining data in order to decode the second frame etc. + * If no frame + * could be outputted, frame_size_ptr is zero. Otherwise, it is the + * decompressed frame size in bytes. + * + * @warning You must set frame_size_ptr to the allocated size of the + * output buffer before calling avcodec_decode_audio3(). * - * @warning The input buffer must be \c FF_INPUT_BUFFER_PADDING_SIZE larger than + * @warning The input buffer must be FF_INPUT_BUFFER_PADDING_SIZE larger than * the actual read bytes because some optimized bitstream readers read 32 or 64 * bits at once and could read over the end. * - * @warning The end of the input buffer \p buf should be set to 0 to ensure that + * @warning The end of the input buffer avpkt->data should be set to 0 to ensure that * no overreading happens for damaged MPEG streams. * - * @note You might have to align the input buffer \p buf and output buffer \p + * @note You might have to align the input buffer avpkt->data and output buffer * samples. The alignment requirements depend on the CPU: On some CPUs it isn't * necessary at all, on others it won't work at all if not aligned and on others - * it will work but it will have an impact on performance. In practice, the - * bitstream should have 4 byte alignment at minimum and all sample data should - * be 16 byte aligned unless the CPU doesn't need it (AltiVec and SSE do). If - * the linesize is not a multiple of 16 then there's no sense in aligning the - * start of the buffer to 16. + * * it will work but it will have an impact on performance. + * + * In practice, avpkt->data should have 4 byte alignment at minimum and + * samples should be 16 byte aligned unless the CPU doesn't need it + * (AltiVec and SSE do). + * + * @note Some codecs have a delay between input and output, these need to be + * feeded with avpkt->data=NULL, avpkt->size=0 at the end to return the remaining frames. * * @param avctx the codec context * @param[out] samples the output buffer * @param[in,out] frame_size_ptr the output buffer size in bytes - * @param[in] buf the input buffer - * @param[in] buf_size the input buffer size in bytes + * @param[in] avpkt The input AVPacket containing the input buffer. + * You can create such packet with av_init_packet() and by then setting + * data and size, some decoders might in addition need other fields. + * All decoders are designed to use the least fields possible though. * @return On error a negative value is returned, otherwise the number of bytes - * used or zero if no frame could be decompressed. + * used or zero if no frame data was decompressed (used) from the input AVPacket. *) -function avcodec_decode_audio2(avctx: PAVCodecContext; samples: PSmallint; +function avcodec_decode_audio3(avctx: PAVCodecContext; samples: PSmallint; var frame_size_ptr: cint; - buf: {const} pchar; buf_size: cint): cint; + avpkt: PAVPacket): cint; cdecl; external av__codec; {$IFEND} +{$IF LIBAVCODEC_MAX_VERSION_MAJOR < 53} +(** + * Decodes a video frame from buf into picture. + * Wrapper function which calls avcodec_decode_video2. + * + * @deprecated Use avcodec_decode_video2 instead. + * @param avctx the codec context + * @param[out] picture The AVFrame in which the decoded video frame will be stored. + * @param[in] buf the input buffer + * @param[in] buf_size the size of the input buffer in bytes + * @param[in,out] got_picture_ptr Zero if no frame could be decompressed, otherwise, it is nonzero. + * @return On error a negative value is returned, otherwise the number of bytes + * used or zero if no frame could be decompressed. + *) +function avcodec_decode_video(avctx: PAVCodecContext; picture: PAVFrame; + var got_picture_ptr: cint; + buf: {const} PByteArray; buf_size: cint): cint; + cdecl; external av__codec; {deprecated;} +{$IFEND} + +{$IF LIBAVCODEC_VERSION >= 52025000} // 52.25.0 (** - * Decodes a video frame from \p buf into \p picture. - * The avcodec_decode_video() function decodes a video frame from the input - * buffer \p buf of size \p buf_size. To decode it, it makes use of the - * video codec which was coupled with \p avctx using avcodec_open(). The - * resulting decoded frame is stored in \p picture. + * Decodes the video frame of size avpkt->size from avpkt->data into picture. + * Some decoders may support multiple frames in a single AVPacket, such + * decoders would then just decode the first frame. * - * @warning The input buffer must be \c FF_INPUT_BUFFER_PADDING_SIZE larger than + * @warning The input buffer must be FF_INPUT_BUFFER_PADDING_SIZE larger than * the actual read bytes because some optimized bitstream readers read 32 or 64 * bits at once and could read over the end. * - * @warning The end of the input buffer \p buf should be set to 0 to ensure that + * @warning The end of the input buffer buf should be set to 0 to ensure that * no overreading happens for damaged MPEG streams. * - * @note You might have to align the input buffer \p buf and output buffer \p - * samples. The alignment requirements depend on the CPU: on some CPUs it isn't + * @note You might have to align the input buffer avpkt->data. + * The alignment requirements depend on the CPU: on some CPUs it isn't * necessary at all, on others it won't work at all if not aligned and on others - * it will work but it will have an impact on performance. In practice, the - * bitstream should have 4 byte alignment at minimum and all sample data should - * be 16 byte aligned unless the CPU doesn't need it (AltiVec and SSE do). If - * the linesize is not a multiple of 16 then there's no sense in aligning the - * start of the buffer to 16. + * it will work but it will have an impact on performance. + * + * In practice, avpkt->data should have 4 byte alignment at minimum. * * @param avctx the codec context * @param[out] picture The AVFrame in which the decoded video frame will be stored. - * @param[in] buf the input buffer - * @param[in] buf_size the size of the input buffer in bytes + * @param[in] avpkt The input AVpacket containing the input buffer. + * You can create such packet with av_init_packet() and by then setting + * data and size, some decoders might in addition need other fields like + * flags&PKT_FLAG_KEY. All decoders are designed to use the least + * fields possible. * @param[in,out] got_picture_ptr Zero if no frame could be decompressed, otherwise, it is nonzero. * @return On error a negative value is returned, otherwise the number of bytes * used or zero if no frame could be decompressed. *) -function avcodec_decode_video(avctx: PAVCodecContext; picture: PAVFrame; +function avcodec_decode_video2(avctx: PAVCodecContext; picture: PAVFrame; var got_picture_ptr: cint; - buf: {const} PChar; buf_size: cint): cint; + avpkt: PAVPacket): cint; cdecl; external av__codec; +{$IFEND} +{$IF LIBAVCODEC_MAX_VERSION_MAJOR < 53} (* Decode a subtitle message. Return -1 if error, otherwise return the * number of bytes used. If no subtitle could be decompressed, - * got_sub_ptr is zero. Otherwise, the subtitle is stored in *sub. *) + * got_sub_ptr is zero. Otherwise, the subtitle is stored in*sub. + *) function avcodec_decode_subtitle(avctx: PAVCodecContext; sub: PAVSubtitle; var got_sub_ptr: cint; - buf: {const} pchar; buf_size: cint): cint; + buf: {const} PByteArray; buf_size: cint): cint; + cdecl; external av__codec; +{$IFEND} + +{$IF LIBAVCODEC_VERSION >= 52025000} // 52.25.0 +(* Decodes a subtitle message. + * Returns a negative value on error, otherwise returns the number of bytes used. + * If no subtitle could be decompressed, got_sub_ptr is zero. + * Otherwise, the subtitle is stored in sub. + * + * @param avctx the codec context + * @param[out] sub The AVSubtitle in which the decoded subtitle will be stored. + * @param[in,out] got_sub_ptr Zero if no subtitle could be decompressed, otherwise, it is nonzero. + * @param[in] avpkt The input AVPacket containing the input buffer. + *) +function avcodec_decode_subtitle2(avctx: PAVCodecContext; sub: PAVSubtitle; + var got_sub_ptr: cint; + avpkt: PAVPacket): cint; cdecl; external av__codec; +{$IFEND} + function avcodec_parse_frame(avctx: PAVCodecContext; pdata: PPointer; data_size_ptr: PCint; - buf: pchar; buf_size: cint): cint; + buf: PByteArray; buf_size: cint): cint; cdecl; external av__codec; (** - * Encodes an audio frame from \p samples into \p buf. - * The avcodec_encode_audio() function encodes an audio frame from the input - * buffer \p samples. To encode it, it makes use of the audio codec which was - * coupled with \p avctx using avcodec_open(). The resulting encoded frame is - * stored in output buffer \p buf. + * Encodes an audio frame from samples into buf. * - * @note The output buffer should be at least \c FF_MIN_BUFFER_SIZE bytes large. + * @note The output buffer should be at least FF_MIN_BUFFER_SIZE bytes large. + * However, for PCM audio the user will know how much space is needed + * because it depends on the value passed in buf_size as described + * below. In that case a lower value can be used. * * @param avctx the codec context * @param[out] buf the output buffer * @param[in] buf_size the output buffer size * @param[in] samples the input buffer containing the samples * The number of samples read from this buffer is frame_size*channels, - * both of which are defined in \p avctx. - * For PCM audio the number of samples read from \p samples is equal to - * \p buf_size * input_sample_size / output_sample_size. + * both of which are defined in avctx. + * For PCM audio the number of samples read from samples is equal to + * buf_size * input_sample_size / output_sample_size. * @return On error a negative value is returned, on success zero or the number * of bytes used to encode the data read from the input buffer. *) @@ -3013,30 +3859,38 @@ function avcodec_encode_audio(avctx: PAVCodecContext; buf: PByte; cdecl; external av__codec; (** - * Encodes a video frame from \p pict into \p buf. - * The avcodec_encode_video() function encodes a video frame from the input - * \p pict. To encode it, it makes use of the video codec which was coupled with - * \p avctx using avcodec_open(). The resulting encoded bytes representing the - * frame are stored in the output buffer \p buf. The input picture should be - * stored using a specific format, namely \c avctx.pix_fmt. + * Encodes a video frame from pict into buf. + * The input picture should be + * stored using a specific format, namely avctx.pix_fmt. * * @param avctx the codec context * @param[out] buf the output buffer for the bitstream of encoded frame * @param[in] buf_size the size of the output buffer in bytes * @param[in] pict the input picture to encode * @return On error a negative value is returned, on success zero or the number - * of bytes used from the input buffer. + * of bytes used from the output buffer. *) function avcodec_encode_video(avctx: PAVCodecContext; buf: PByte; buf_size: cint; pict: PAVFrame): cint; cdecl; external av__codec; -function avcodec_encode_subtitle(avctx: PAVCodecContext; buf: pchar; +function avcodec_encode_subtitle(avctx: PAVCodecContext; buf: PByteArray; buf_size: cint; sub: {const} PAVSubtitle): cint; cdecl; external av__codec; function avcodec_close(avctx: PAVCodecContext): cint; cdecl; external av__codec; +(** + * Register all the codecs, parsers and bitstream filters which were enabled at + * configuration time. If you do not call this function you can select exactly + * which formats you want to support, by using the individual registration + * functions. + * + * @see register_avcodec + * @see avcodec_register + * @see av_register_codec_parser + * @see av_register_bitstream_filter + *) procedure avcodec_register_all(); cdecl; external av__codec; @@ -3052,12 +3906,12 @@ procedure avcodec_default_free_buffers(s: PAVCodecContext); (* misc useful functions *) (** - * Returns a single letter to describe the given picture type \p pict_type. + * Returns a single letter to describe the given picture type pict_type. * * @param[in] pict_type the picture type * @return A single character representing the picture type. *) -function av_get_pict_type_char(pict_type: cint): char; +function av_get_pict_type_char(pict_type: cint): AnsiChar; cdecl; external av__codec; (** @@ -3097,6 +3951,15 @@ type next_frame_offset: cint64; (* offset of the next frame *) (* video info *) pict_type: cint; (* XXX: put it back in AVCodecContext *) + (** + * This field is used for proper frame duration computation in lavf. + * It signals, how much longer the frame duration of the current frame + * is compared to normal frame duration. + * + * frame_duration = (1 + repeat_pict) * time_base + * + * It is used by codecs like H.264 to display telecined material. + *) repeat_pict: cint; (* XXX: put it back in AVCodecContext *) pts: cint64; (* pts of the current frame *) dts: cint64; (* dts of the current frame *) @@ -3119,6 +3982,94 @@ type {$IF LIBAVCODEC_VERSION >= 51057001} // 51.57.1 cur_frame_end: array [0..AV_PARSER_PTS_NB - 1] of cint64; {$IFEND} + {$IF LIBAVCODEC_VERSION >= 52016000} // 52.16.0 + (*! + * Set by parser to 1 for key frames and 0 for non-key frames. + * It is initialized to -1, so if the parser doesn't set this flag, + * old-style fallback using FF_I_TYPE picture type as key frames + * will be used. + *) + key_frame: cint; + {$IFEND} + {$IF LIBAVCODEC_VERSION >= 52018000} // 52.18.0 + (** + * Time difference in stream time base units from the pts of this + * packet to the point at which the output from the decoder has converged + * independent from the availability of previous frames. That is, the + * frames are virtually identical no matter if decoding started from + * the very first frame or from this keyframe. + * Is AV_NOPTS_VALUE if unknown. + * This field is not the display duration of the current frame. + * + * The purpose of this field is to allow seeking in streams that have no + * keyframes in the conventional sense. It corresponds to the + * recovery point SEI in H.264 and match_time_delta in NUT. It is also + * essential for some types of subtitle streams to ensure that all + * subtitles are correctly displayed after seeking. + *) + convergence_duration: cint64; + {$IFEND} + {$IF LIBAVCODEC_VERSION >= 52019000} // 52.19.0 + // Timestamp generation support: + (** + * Synchronization point for start of timestamp generation. + * + * Set to >0 for sync point, 0 for no sync point and <0 for undefined + * (default). + * + * For example, this corresponds to presence of H.264 buffering period + * SEI message. + *) + dts_sync_point: cint; + + (** + * Offset of the current timestamp against last timestamp sync point in + * units of AVCodecContext.time_base. + * + * Set to INT_MIN when dts_sync_point unused. Otherwise, it must + * contain a valid timestamp offset. + * + * Note that the timestamp of sync point has usually a nonzero + * dts_ref_dts_delta, which refers to the previous sync point. Offset of + * the next frame after timestamp sync point will be usually 1. + * + * For example, this corresponds to H.264 cpb_removal_delay. + *) + dts_ref_dts_delta: cint; + + (** + * Presentation delay of current frame in units of AVCodecContext.time_base. + * + * Set to INT_MIN when dts_sync_point unused. Otherwise, it must + * contain valid non-negative timestamp delta (presentation time of a frame + * must not lie in the past). + * + * This delay represents the difference between decoding and presentation + * time of the frame. + * + * For example, this corresponds to H.264 dpb_output_delay. + *) + pts_dts_delta: cint; + {$IFEND} + + {$IF LIBAVCODEC_VERSION >= 52021000} // 52.21.0 + (** + * Position of the packet in file. + * + * Analogous to cur_frame_pts/dts + *) + cur_frame_pos: array [0..AV_PARSER_PTS_NB - 1] of cint64; + + (** + * Byte position of currently parsed frame in stream. + *) + pos: cint64; + + (** + * Previous frame byte position. + *) + last_pos: cint64; + {$IFEND} end; TAVCodecParser = record @@ -3127,9 +4078,9 @@ type parser_init: function(s: PAVCodecParserContext): cint; cdecl; parser_parse: function(s: PAVCodecParserContext; avctx: PAVCodecContext; poutbuf: {const} PPointer; poutbuf_size: PCint; - buf: {const} pchar; buf_size: cint): cint; cdecl; + buf: {const} PByteArray; buf_size: cint): cint; cdecl; parser_close: procedure(s: PAVCodecParserContext); cdecl; - split: function(avctx: PAVCodecContext; buf: {const} pchar; + split: function(avctx: PAVCodecContext; buf: {const} PByteArray; buf_size: cint): cint; cdecl; next: PAVCodecParser; end; @@ -3153,16 +4104,64 @@ procedure av_register_codec_parser(parser: PAVCodecParser); function av_parser_init(codec_id: cint): PAVCodecParserContext; cdecl; external av__codec; +{$IF LIBAVCODEC_MAX_VERSION_MAJOR < 53} function av_parser_parse(s: PAVCodecParserContext; avctx: PAVCodecContext; - poutbuf: PPointer; poutbuf_size: PCint; - buf: {const} pchar; buf_size: cint; - pts: cint64; dts: cint64): cint; - cdecl; external av__codec; + poutbuf: PPointer; + poutbuf_size: PCint; + buf: {const} PByteArray; + buf_size: cint; + pts: cint64; + dts: cint64): cint; + cdecl; external av__codec; deprecated; +{$IFEND} + +{$IF LIBAVCODEC_VERSION >= 52021000} // 52.21.0 +(** + * Parse a packet. + * + * @param s parser context. + * @param avctx codec context. + * @param poutbuf set to pointer to parsed buffer or NULL if not yet finished. + * @param poutbuf_size set to size of parsed buffer or zero if not yet finished. + * @param buf input buffer. + * @param buf_size input length, to signal EOF, this should be 0 (so that the last frame can be output). + * @param pts input presentation timestamp. + * @param dts input decoding timestamp. + * @param pos input byte position in stream. + * @return the number of bytes of the input bitstream used. + * + * Example: + * @code + * while (in_len) do + * begin + * len := av_parser_parse2(myparser, AVCodecContext, data, size, + * in_data, in_len, + * pts, dts, pos); + * in_data := in_data + len; + * in_len := in_len - len; + * + * if (size) then + * decode_frame(data, size); + * end; + * @endcode + *) +function av_parser_parse2(s: PAVCodecParserContext; + avctx: PAVCodecContext; + poutbuf: PPointer; + poutbuf_size: PCint; + buf: {const} PByteArray; + buf_size: cint; + pts: cint64; + dts: cint64; + pos: cint64): cint; + cdecl; external av__codec; +{$IFEND} + function av_parser_change(s: PAVCodecParserContext; avctx: PAVCodecContext; poutbuf: PPointer; poutbuf_size: PCint; - buf: {const} pchar; buf_size: cint; keyframe: cint): cint; + buf: {const} PByteArray; buf_size: cint; keyframe: cint): cint; cdecl; external av__codec; procedure av_parser_close(s: PAVCodecParserContext); cdecl; external av__codec; @@ -3179,10 +4178,10 @@ type end; TAVBitStreamFilter = record - name: pchar; + name: PAnsiChar; priv_data_size: cint; filter: function(bsfc: PAVBitStreamFilterContext; - avctx: PAVCodecContext; args: pchar; + avctx: PAVCodecContext; args: PByteArray; poutbuf: PPointer; poutbuf_size: PCint; buf: PByte; buf_size: cint; keyframe: cint): cint; cdecl; {$IF LIBAVCODEC_VERSION >= 51043000} // 51.43.0 @@ -3194,11 +4193,11 @@ type procedure av_register_bitstream_filter(bsf: PAVBitStreamFilter); cdecl; external av__codec; -function av_bitstream_filter_init(name: pchar): PAVBitStreamFilterContext; +function av_bitstream_filter_init(name: PAnsiChar): PAVBitStreamFilterContext; cdecl; external av__codec; function av_bitstream_filter_filter(bsfc: PAVBitStreamFilterContext; - avctx: PAVCodecContext; args: pchar; + avctx: PAVCodecContext; args: PByteArray; poutbuf: PPointer; poutbuf_size: PCint; buf: PByte; buf_size: cint; keyframe: cint): cint; cdecl; external av__codec; @@ -3221,6 +4220,22 @@ function av_bitstream_filter_next(f: PAVBitStreamFilter): PAVBitStreamFilter; procedure av_fast_realloc(ptr: pointer; size: PCuint; min_size: cuint); cdecl; external av__codec; +{$IF LIBAVCODEC_VERSION >= 52025000} // >= 52.25.0 +(** + * Allocates a buffer, reusing the given one if large enough. + * + * Contrary to av_fast_realloc the current buffer contents might not be + * preserved and on error the old buffer is freed, thus no special + * handling to avoid memleaks is necessary. + * + * @param ptr pointer to pointer to already allocated buffer, overwritten with pointer to new buffer + * @param size size of the buffer *ptr points to + * @param min_size minimum size of *ptr buffer after returning, *ptr will be NULL and + * *size 0 if an error occurred. + *) +procedure av_fast_malloc(ptr: pointer; size: PCuint; min_size: cuint); + cdecl; external av__codec; +{$IFEND} {$IF LIBAVCODEC_VERSION < 51057000} // 51.57.0 (* for static data only *) @@ -3233,7 +4248,7 @@ procedure av_fast_realloc(ptr: pointer; size: PCuint; min_size: cuint); * and should correctly use static arrays * *) -procedure av_free_static(); +procedure av_free_static(); cdecl; external av__codec; deprecated; (** @@ -3259,22 +4274,49 @@ procedure av_realloc_static(ptr: pointer; size: cuint); (** * Copy image 'src' to 'dst'. *) -procedure av_picture_copy(dst: PAVPicture; src: {const} PAVPicture; - pix_fmt: cint; width: cint; height: cint); +procedure av_picture_copy(dst: PAVPicture; + src: {const} PAVPicture; +{$IF LIBAVCODEC_VERSION < 52022001} // 52.22.1 + pix_fmt: cint; +{$ELSE} + pix_fmt: TAVPixelFormat; +{$IFEND} + width: cint; + height: cint); cdecl; external av__codec; (** * Crop image top and left side. *) -function av_picture_crop(dst: PAVPicture; src: {const} PAVPicture; - pix_fmt: cint; top_band: cint; left_band: cint): cint; +function av_picture_crop(dst: PAVPicture; + src: {const} PAVPicture; +{$IF LIBAVCODEC_VERSION < 52022001} // 52.22.1 + pix_fmt: cint; +{$ELSE} + pix_fmt: TAVPixelFormat; +{$IFEND} + top_band: cint; + left_band: cint): cint; cdecl; external av__codec; (** * Pad image. *) -function av_picture_pad(dst: PAVPicture; src: {const} PAVPicture; height: cint; width: cint; pix_fmt: cint; - padtop: cint; padbottom: cint; padleft: cint; padright: cint; color: PCint): cint; +function av_picture_pad(dst: PAVPicture; + src: {const} PAVPicture; + height: cint; + width: cint; +{$IF LIBAVCODEC_VERSION < 52022001} // 52.22.1 + pix_fmt: cint; +{$ELSE} + pix_fmt: TAVPixelFormat; +{$IFEND} + padtop: cint; + padbottom: cint; + padleft: cint; + padright: + cint; + color: PCint): cint; cdecl; external av__codec; {$IFEND} @@ -3307,7 +4349,7 @@ function av_xiphlacing(s: PByte; v: cuint): cuint; {$IF LIBAVCODEC_VERSION >= 51041000} // 51.41.0 (** - * Parses \p str and put in \p width_ptr and \p height_ptr the detected values. + * Parses str and put in width_ptr and height_ptr the detected values. * * @return 0 in case of a successful parsing, a negative value otherwise * @param[in] str the string to parse: it has to be a string in the format @@ -3317,34 +4359,19 @@ function av_xiphlacing(s: PByte; v: cuint): cuint; * @param[in,out] height_ptr pointer to the variable which will contain the detected * frame height value *) -function av_parse_video_frame_size(width_ptr: PCint; height_ptr: PCint; str: {const} PChar): cint; +function av_parse_video_frame_size(width_ptr: PCint; height_ptr: PCint; str: {const} PAnsiChar): cint; cdecl; external av__codec; (** - * Parses \p str and put in \p frame_rate the detected values. + * Parses str and put in frame_rate the detected values. * * @return 0 in case of a successful parsing, a negative value otherwise * @param[in] str the string to parse: it has to be a string in the format - * <frame_rate_nom>/<frame_rate_den>, a float number or a valid video rate abbreviation + * <frame_rate_num>/<frame_rate_den>, a float number or a valid video rate abbreviation * @param[in,out] frame_rate pointer to the AVRational which will contain the detected * frame rate *) -function av_parse_video_frame_rate(frame_rate: PAVRational; str: {const} PChar): cint; - cdecl; external av__codec; -{$IFEND} - -{$IF LIBAVCODEC_VERSION >= 51064000} // 51.64.0 -(** - * Logs a generic warning message about a missing feature. - * @param[in] avc a pointer to an arbitrary struct of which the first field is - * a pointer to an AVClass struct - * @param[in] feature string containing the name of the missing feature - * @param[in] want_sample indicates if samples are wanted which exhibit this feature. - * If \p want_sample is non-zero, additional verbage will be added to the log - * message which tells the user how to report samples to the development - * mailing list. - *) -procedure av_log_missing_feature(avc: Pointer; feature: {const} PChar; want_sample: cint); +function av_parse_video_frame_rate(frame_rate: PAVRational; str: {const} PAnsiChar): cint; cdecl; external av__codec; {$IFEND} @@ -3359,11 +4386,13 @@ const EDOM = ESysEDOM; ENOSYS = ESysENOSYS; EILSEQ = ESysEILSEQ; + EPIPE = ESysEPIPE; {$ELSE} ENOENT = 2; EIO = 5; ENOMEM = 12; EINVAL = 22; + EPIPE = 32; // just an assumption. needs to be checked. EDOM = 33; {$IFDEF MSWINDOWS} // Note: we assume that ffmpeg was compiled with MinGW. @@ -3400,11 +4429,101 @@ const AVERROR_NOMEM = AVERROR_SIGN * ENOMEM; (**< not enough memory *) AVERROR_NOFMT = AVERROR_SIGN * EILSEQ; (**< unknown format *) AVERROR_NOTSUPP = AVERROR_SIGN * ENOSYS; (**< Operation not supported. *) - AVERROR_NOENT = AVERROR_SIGN * ENOENT; {**< No such file or directory. *} + AVERROR_NOENT = AVERROR_SIGN * ENOENT; (**< No such file or directory. *) +{$IF LIBAVCODEC_VERSION >= 52017000} // 52.17.0 + AVERROR_EOF = AVERROR_SIGN * EPIPE; (**< End of file. *) +{$IFEND} // Note: function calls as constant-initializers are invalid //AVERROR_PATCHWELCOME = -MKTAG('P','A','W','E'); {**< Not yet implemented in FFmpeg. Patches welcome. *} AVERROR_PATCHWELCOME = -(ord('P') or (ord('A') shl 8) or (ord('W') shl 16) or (ord('E') shl 24)); +{$IF LIBAVCODEC_VERSION >= 52032000} // >= 52.32.0 +(** + * Logs a generic warning message about a missing feature. This function is + * intended to be used internally by FFmpeg (libavcodec, libavformat, etc.) + * only, and would normally not be used by applications. + * @param[in] avc a pointer to an arbitrary struct of which the first field is + * a pointer to an AVClass struct + * @param[in] feature string containing the name of the missing feature + * @param[in] want_sample indicates if samples are wanted which exhibit this feature. + * If want_sample is non-zero, additional verbage will be added to the log + * message which tells the user how to report samples to the development + * mailing list. + *) +procedure av_log_missing_feature(avc: Pointer; feature: {const} Pchar; want_sample: cint); + cdecl; external av__codec; + +(** + * Logs a generic warning message asking for a sample. This function is + * intended to be used internally by FFmpeg (libavcodec, libavformat, etc.) + * only, and would normally not be used by applications. + * @param[in] avc a pointer to an arbitrary struct of which the first field is + * a pointer to an AVClass struct + * @param[in] msg string containing an optional message, or NULL if no message + *) +procedure av_log_ask_for_sample(avc: Pointer; msg: {const} Pchar); + cdecl; external av__codec; +{$IFEND} + +{$IF LIBAVCODEC_VERSION >= 52018000} // 52.18.0 +(** + * Registers the hardware accelerator hwaccel. + *) +procedure av_register_hwaccel (hwaccel: PAVHWAccel) + cdecl; external av__codec; + +(** + * If hwaccel is NULL, returns the first registered hardware accelerator, + * if hwaccel is non-NULL, returns the next registered hardware accelerator + * after hwaccel, or NULL if hwaccel is the last one. + *) +function av_hwaccel_next (hwaccel: PAVHWAccel): PAVHWAccel; + cdecl; external av__codec; +{$IFEND} + +{$IF LIBAVCODEC_VERSION >= 52030000} // 52.30.0 +(** + * Lock operation used by lockmgr + *) +type + TAVLockOp = ( + AV_LOCK_CREATE, ///< Create a mutex + AV_LOCK_OBTAIN, ///< Lock the mutex + AV_LOCK_RELEASE, ///< Unlock the mutex + AV_LOCK_DESTROY ///< Free mutex resources + ); + +(** + * Register a user provided lock manager supporting the operations + * specified by AVLockOp. mutex points to a (void) where the + * lockmgr should store/get a pointer to a user allocated mutex. It's + * NULL upon AV_LOCK_CREATE and != NULL for all other ops. + * + * @param cb User defined callback. Note: FFmpeg may invoke calls to this + * callback during the call to av_lockmgr_register(). + * Thus, the application must be prepared to handle that. + * If cb is set to NULL the lockmgr will be unregistered. + * Also note that during unregistration the previously registered + * lockmgr callback may also be invoked. + *) +// ToDo: Implement and test this +//function av_lockmgr_register(cb: function (mutex: pointer; op: TAVLockOp)): cint; +// cdecl; external av__codec; +{$IFEND} + implementation +{$IF (LIBAVCODEC_VERSION >= 52025000) and (LIBAVCODEC_VERSION <= 52027000)} // 52.25.0 - 52.27.0 +procedure av_free_packet(pkt: PAVPacket);{$IFDEF HASINLINE} inline; {$ENDIF} +begin + if assigned(pkt) then + begin + if assigned(pkt^.destruct) then + pkt^.destruct(pkt); + pkt^.data := NIL; + pkt^.size := 0; + end; +end; +{$IFEND} + end. diff --git a/Lua/src/lib/ffmpeg/avformat.pas b/Lua/src/lib/ffmpeg/avformat.pas index c516aea0..2b8a5e4f 100644 --- a/Lua/src/lib/ffmpeg/avformat.pas +++ b/Lua/src/lib/ffmpeg/avformat.pas @@ -27,8 +27,13 @@ (* * Conversion of libavformat/avformat.h * Min. version: 50.5.0 , revision 6577, Sat Oct 7 15:30:46 2006 UTC - * Max. version: 52.22.1, revision 15441, Sat Sep 27 20:05:12 2008 UTC + * Max. version: 52.25.0, revision 16986, Wed Feb 4 05:56:39 2009 UTC *) +{ + * update to + * Max. version: 52.41.0, Sun Dec 6 20:15:00 2009 CET + * MiSchi +} unit avformat; @@ -54,13 +59,14 @@ uses avutil, avio, rational, + SysUtils, UConfig; const (* Max. supported version by this header *) LIBAVFORMAT_MAX_VERSION_MAJOR = 52; - LIBAVFORMAT_MAX_VERSION_MINOR = 22; - LIBAVFORMAT_MAX_VERSION_RELEASE = 1; + LIBAVFORMAT_MAX_VERSION_MINOR = 41; + LIBAVFORMAT_MAX_VERSION_RELEASE = 0; LIBAVFORMAT_MAX_VERSION = (LIBAVFORMAT_MAX_VERSION_MAJOR * VERSION_MAJOR) + (LIBAVFORMAT_MAX_VERSION_MINOR * VERSION_MINOR) + (LIBAVFORMAT_MAX_VERSION_RELEASE * VERSION_RELEASE); @@ -91,33 +97,105 @@ function avformat_version(): cuint; cdecl; external av__format; {$IFEND} +{$IF LIBAVFORMAT_VERSION >= 52039002} // 52.39.2 +(** + * Returns the libavformat build-time configuration. + *) +function avformat_configuration(): {const} PansiChar; + cdecl; external av__format; + +(** + * Returns the libavformat license. + *) +function avformat_license(): {const} PansiChar; + cdecl; external av__format; +{$IFEND} type PAVFile = Pointer; +(* + * Public Metadata API. + * The metadata API allows libavformat to export metadata tags to a client + * application using a sequence of key/value pairs. + * Important concepts to keep in mind: + * 1. Keys are unique; there can never be 2 tags with the same key. This is + * also meant semantically, i.e., a demuxer should not knowingly produce + * several keys that are literally different but semantically identical. + * E.g., key=Author5, key=Author6. In this example, all authors must be + * placed in the same tag. + * 2. Metadata is flat, not hierarchical; there are no subtags. If you + * want to store, e.g., the email address of the child of producer Alice + * and actor Bob, that could have key=alice_and_bobs_childs_email_address. + * 3. A tag whose value is localized for a particular language is appended + * with a dash character ('-') and the ISO 639-2/B 3-letter language code. + * For example: Author-ger=Michael, Author-eng=Mike + * The original/default language is in the unqualified "Author" tag. + * A demuxer should set a default if it sets any translated tag. + *) +const + AV_METADATA_MATCH_CASE = 1; + AV_METADATA_IGNORE_SUFFIX = 2; + +type + PAVMetadataTag = ^TAVMetadataTag; + TAVMetadataTag = record + key: PAnsiChar; + value: PAnsiChar; + end; + + PAVMetadata = Pointer; + +{$IF LIBAVFORMAT_VERSION > 52024001} // > 52.24.1 +(** + * Gets a metadata element with matching key. + * @param prev Set to the previous matching element to find the next. + * @param flags Allows case as well as suffix-insensitive comparisons. + * @return Found tag or NULL, changing key or value leads to undefined behavior. + *) +function av_metadata_get(m: PAVMetadata; key: {const} PAnsiChar; + prev: {const} PAVMetadataTag ; flags: cint): PAVMetadataTag; + cdecl; external av__format; + +(** + * Sets the given tag in m, overwriting an existing tag. + * @param key tag key to add to m (will be av_strduped) + * @param value tag value to add to m (will be av_strduped) + * @return >= 0 on success otherwise an error code <0 + *) +function av_metadata_set(var pm: PAVMetadata; key: {const} PAnsiChar; value: {const} PAnsiChar): cint; + cdecl; external av__format; + +(** + * Frees all the memory allocated for an AVMetadata struct. + *) +procedure av_metadata_free(var m: PAVMetadata); + cdecl; external av__format; +{$IFEND} + (* packet functions *) +{$IF LIBAVCODEC_VERSION < 52032000} // < 52.32.0 type PAVPacket = ^TAVPacket; TAVPacket = record (** - * Presentation time stamp in time_base units. - * This is the time at which the decompressed packet will be presented - * to the user. + * Presentation timestamp in time_base units; the time at which the + * decompressed packet will be presented to the user. * Can be AV_NOPTS_VALUE if it is not stored in the file. * pts MUST be larger or equal to dts as presentation can not happen before * decompression, unless one wants to view hex dumps. Some formats misuse - * the terms dts and pts/cts to mean something different, these timestamps + * the terms dts and pts/cts to mean something different. Such timestamps * must be converted to true pts/dts before they are stored in AVPacket. *) pts: cint64; (** - * Decompression time stamp in time_base units. - * This is the time at which the packet is decompressed. + * Decompression timestamp in time_base units; the time at which the + * packet is decompressed. * Can be AV_NOPTS_VALUE if it is not stored in the file. *) dts: cint64; - data: PChar; + data: PByteArray; size: cint; stream_index: cint; flags: cint; @@ -126,7 +204,7 @@ type * Equals next_pts - this_pts in presentation order. *) duration: cint; - destruct: procedure (p: PAVPacket); cdecl; + destruct: procedure (p: PAVPacket); cdecl; priv: pointer; pos: cint64; ///< byte position in stream, -1 if unknown @@ -182,6 +260,7 @@ procedure av_init_packet(var pkt: TAVPacket); *) function av_new_packet(var pkt: TAVPacket; size: cint): cint; cdecl; external av__format; +{$IFEND} (** * Allocate and read the payload of a packet and initialize its fields with @@ -194,6 +273,7 @@ function av_new_packet(var pkt: TAVPacket; size: cint): cint; function av_get_packet(s: PByteIOContext; var pkt: TAVPacket; size: cint): cint; cdecl; external av__format; +{$IF LIBAVCODEC_VERSION < 52032000} // < 52.32.0 (** * @warning This is a hack - the packet memory allocation stuff is broken. The * packet is allocated if it was not really allocated. @@ -207,6 +287,7 @@ function av_dup_packet(pkt: PAVPacket): cint; * @param pkt packet to free *) procedure av_free_packet(pkt: PAVPacket); {$IFDEF HasInline}inline;{$ENDIF} +{$IFEND} (*************************************************) (* fractional numbers for exact pts handling *) @@ -215,12 +296,11 @@ type (** * The exact value of the fractional number is: 'val + num / den'. * num is assumed to be 0 <= num < den. - * @deprecated Use AVRational instead. *) PAVFrac = ^TAVFrac; TAVFrac = record val, num, den: cint64; - end; {deprecated} + end; (*************************************************) (* input/output formats *) @@ -228,13 +308,13 @@ type type (** This structure contains the data a format has to probe a file. *) TAVProbeData = record - filename: pchar; - buf: pchar; - buf_size: cint; + filename: PAnsiChar; + buf: PByteArray; (**< Buffer must have AVPROBE_PADDING_SIZE of extra allocated bytes filled with zero. *) + buf_size: cint; (**< Size of buf except extra allocated bytes *) end; const - AVPROBE_SCORE_MAX = 100; ///< Maximum score, half of that is used for file-extension-based detection. + AVPROBE_SCORE_MAX = 100; ///< Maximum score, half of that is used for file-extension-based detection AVPROBE_PADDING_SIZE = 32; ///< extra allocated bytes at the end of the probe buffer //! Demuxer will use url_fopen, no opened file should be provided by the caller. @@ -247,6 +327,9 @@ const AVFMT_NOTIMESTAMPS = $0080; (**< Format does not need / have any timestamps. *) AVFMT_GENERIC_INDEX = $0100; (**< Use generic index building code. *) AVFMT_TS_DISCONT = $0200; (**< Format allows timestamp discontinuities. *) + {$IF LIBAVFORMAT_VERSION >= 52029002} // 52.29.2 + AVFMT_VARIABLE_FPS = $0400; (**< Format allows variable fps. *) + {$IFEND} // used by AVIndexEntry AVINDEX_KEYFRAME = $0001; @@ -257,7 +340,7 @@ const AVFMT_NOOUTPUTLOOP = -1; AVFMT_INFINITEOUTPUTLOOP = 0; - AVFMT_FLAG_GENPTS = $0001; ///< Generate pts if missing even if it requires parsing future frames. + AVFMT_FLAG_GENPTS = $0001; ///< Generate missing pts even if it requires parsing future frames. AVFMT_FLAG_IGNIDX = $0002; ///< Ignore index. AVFMT_FLAG_NONBLOCK = $0004; ///< Do not block when reading packets from input. @@ -276,7 +359,23 @@ const AV_DISPOSITION_KARAOKE = $0020; // used by TAVFormatContext.debug - FF_FDEBUG_TS = 0001; + FF_FDEBUG_TS = 0001; + + {$IF LIBAVFORMAT_VERSION >= 52034000} // >= 52.34.0 + {$IF LIBAVFORMAT_VERSION < 52039000} // < 52.39.0 + MAX_PROBE_PACKETS = 100; + {$ELSE} + MAX_PROBE_PACKETS = 2500; + {$IFEND} + {$IFEND} + + {$IF LIBAVFORMAT_VERSION >= 52035000} // >= 52.35.0 + {$IF LIBAVFORMAT_VERSION < 52039000} // < 52.39.0 + RAW_PACKET_BUFFER_SIZE 32000 + {$ELSE} + RAW_PACKET_BUFFER_SIZE 2500000 + {$IFEND} + {$IFEND} type PPAVCodecTag = ^PAVCodecTag; @@ -304,12 +403,32 @@ type PAVImageInfo = ^TAVImageInfo; {$IFEND} +{$IF LIBAVFORMAT_VERSION >= 52030001} // >= 52.30.1 +(** + * Convert all the metadata sets from ctx according to the source and + * destination conversion tables. + * @param d_conv destination tags format conversion table + * @param s_conv source tags format conversion table + *) + PAVMetadataConv = ^TAVMetadataConv; + TAVMetadataConv = record + ctx: PAVFormatContext; + d_conv: {const} PAVMetadataConv; + s_conv: {const} PAVMetadataConv; + end; +{$IFEND} + PAVChapter = ^TAVChapter; TAVChapter = record id: cint; ///< unique ID to identify the chapter time_base: TAVRational; ///< time base in which the start/end timestamps are specified start, end_: cint64; ///< chapter start/end time in time_base units - title: PChar; ///< chapter title + {$IF LIBAVFORMAT_VERSION < 53000000} // 53.00.0 + title: PAnsiChar; ///< chapter title + {$IFEND} + {$IF LIBAVFORMAT_VERSION >= 52024001} // 52.24.1 + metadata: PAVMetadata; + {$IFEND} end; TAVChapterArray = array[0..(MaxInt div SizeOf(TAVChapter))-1] of TAVChapter; PAVChapterArray = ^TAVChapterArray; @@ -326,9 +445,9 @@ type {$IFEND} channel: cint; (**< Used to select DV channel. *) {$IF LIBAVFORMAT_VERSION_MAJOR < 52} - device: pchar; (* video, audio or DV device, if LIBAVFORMAT_VERSION_INT < (52<<16) *) + device: PAnsiChar; (* video, audio or DV device, if LIBAVFORMAT_VERSION_INT < (52<<16) *) {$IFEND} - standard: pchar; (**< TV standard, NTSC, PAL, SECAM *) + standard: PAnsiChar; (**< TV standard, NTSC, PAL, SECAM *) { Delphi does not support bit fields -> use bf_flags instead unsigned int mpeg2ts_raw:1; (**< Force raw MPEG-2 transport stream output, if possible. *) unsigned int mpeg2ts_compute_pcr:1; (**< Compute exact PCR for each transport @@ -346,16 +465,16 @@ type end; TAVOutputFormat = record - name: pchar; + name: PAnsiChar; (** * Descriptive name for the format, meant to be more human-readable - * than \p name. You \e should use the NULL_IF_CONFIG_SMALL() macro + * than name. You should use the NULL_IF_CONFIG_SMALL() macro * to define it. *) - long_name: pchar; - mime_type: pchar; - extensions: pchar; (**< comma-separated filename extensions *) - (** Size of private data so that it can be allocated in the wrapper. *) + long_name: PAnsiChar; + mime_type: PAnsiChar; + extensions: PAnsiChar; (**< comma-separated filename extensions *) + (** size of private data so that it can be allocated in the wrapper *) priv_data_size: cint; (* output support *) audio_codec: TCodecID; (**< default audio codec *) @@ -373,7 +492,7 @@ type {$IF LIBAVFORMAT_VERSION >= 51008000} // 51.8.0 (** * List of supported codec_id-codec_tag pairs, ordered by "better - * choice first". The arrays are all CODEC_ID_NONE terminated. + * choice first". The arrays are all terminated by CODEC_ID_NONE. *) codec_tag: {const} PPAVCodecTag; {$IFEND} @@ -382,22 +501,26 @@ type subtitle_codec: TCodecID; (**< default subtitle codec *) {$IFEND} + {$IF LIBAVFORMAT_VERSION >= 52030001} // 52.30.1 + {const} metadata_conv: PAVMetadataConv; + {$IFEND} + (* private fields *) next: PAVOutputFormat; end; TAVInputFormat = record - name: pchar; + name: PAnsiChar; (** * Descriptive name for the format, meant to be more human-readable - * than \p name. You \e should use the NULL_IF_CONFIG_SMALL() macro + * than name. You should use the NULL_IF_CONFIG_SMALL() macro * to define it. *) - long_name: pchar; + long_name: PAnsiChar; (** Size of private data so that it can be allocated in the wrapper. *) priv_data_size: cint; (** - * Tell if a given file has a chance of being parsed by this format. + * Tell if a given file has a chance of being parsed as this format. * The buffer provided is guaranteed to be AVPROBE_PADDING_SIZE bytes * big so you do not have to check for that unless you need more. *) @@ -409,21 +532,28 @@ type read_header: function (c: PAVFormatContext; ap: PAVFormatParameters): cint; cdecl; (** Read one packet and put it in 'pkt'. pts and flags are also set. 'av_new_stream' can be called only if the flag - AVFMTCTX_NOHEADER is used. *) + AVFMTCTX_NOHEADER is used. + @return 0 on success, < 0 on error. + When returning an error, pkt must not have been allocated + or must be freed before returning *) read_packet: function (c: PAVFormatContext; var pkt: TAVPacket): cint; cdecl; (** Close the stream. The AVFormatContext and AVStreams are not freed by this function *) read_close: function (c: PAVFormatContext): cint; cdecl; + +{$IF LIBAVFORMAT_VERSION_MAJOR < 53} (** * Seek to a given timestamp relative to the frames in * stream component stream_index. - * @param stream_index must not be -1 - * @param flags selects which direction should be preferred if no exact - * match is available + * @param stream_index Must not be -1. + * @param flags Selects which direction should be preferred if no exact + * match is available. * @return >= 0 on success (but not necessarily the new offset) *) read_seek: function (c: PAVFormatContext; stream_index: cint; timestamp: cint64; flags: cint): cint; cdecl; +{$IFEND} + (** * Gets the next timestamp in stream[stream_index].time_base units. * @return the timestamp or AV_NOPTS_VALUE if an error occurred @@ -435,15 +565,15 @@ type (** If extensions are defined, then no probe is done. You should usually not use extension format guessing because it is not reliable enough *) - extensions: pchar; + extensions: PAnsiChar; (** General purpose read-only value that the format can use. *) value: cint; - (** Start/resume playing - only meaningful if using a network-based format + (** Starts/resumes playing - only meaningful if using a network-based format (RTSP). *) read_play: function (c: PAVFormatContext): cint; cdecl; - (** Pause playing - only meaningful if using a network-based format + (** Pauses playing - only meaningful if using a network-based format (RTSP). *) read_pause: function (c: PAVFormatContext): cint; cdecl; @@ -451,6 +581,25 @@ type codec_tag: {const} PPAVCodecTag; {$IFEND} + {$IF LIBAVFORMAT_VERSION >= 52030000} // 52.30.0 + (** + * Seeks to timestamp ts. + * Seeking will be done so that the point from which all active streams + * can be presented successfully will be closest to ts and within min/max_ts. + * Active streams are all streams that have AVStream.discard < AVDISCARD_ALL. + *) + read_seek2: function (s: PAVFormatContext; + stream_index: cint; + min_ts: cint64; + ts: cint64; + max_ts: cint64; + flags: cint): cint; cdecl; + {$IFEND} + + {$IF LIBAVFORMAT_VERSION >= 52030001} // 52.30.1 + {const} metadata_conv: PAVMetadataConv; + {$IFEND} + (* private fields *) next: PAVInputFormat; end; @@ -485,11 +634,11 @@ type id: cint; (**< format-specific stream ID *) codec: PAVCodecContext; (**< codec context *) (** - * Real base frame rate of the stream. - * This is the lowest frame rate with which all timestamps can be + * Real base framerate of the stream. + * This is the lowest framerate with which all timestamps can be * represented accurately (it is the least common multiple of all - * frame rates in the stream). Note, this value is just a guess! - * For example if the timebase is 1/90000 and all frames have either + * framerates in the stream). Note, this value is just a guess! + * For example, if the time base is 1/90000 and all frames have either * approximately 3600 or 1800 timer ticks, then r_frame_rate will be 50/1. *) r_frame_rate: TAVRational; @@ -506,7 +655,7 @@ type (** * This is the fundamental unit of time (in seconds) in terms * of which frame timestamps are represented. For fixed-fps content, - * time base should be 1/frame rate and timestamp increments should be 1. + * time base should be 1/framerate and timestamp increments should be 1. *) time_base: TAVRational; pts_wrap_bits: cint; (* number of bits in pts (used for wrapping control) *) @@ -533,7 +682,9 @@ type *) duration: cint64; - language: array [0..3] of char; (* ISO 639 3-letter language code (empty string if undefined) *) + {$IF LIBAVFORMAT_VERSION_MAJOR < 53} + language: array [0..3] of PAnsiChar; (* ISO 639-2/B 3-letter language code (empty string if undefined) *) + {$IFEND} (* av_read_frame() support *) need_parsing: TAVStreamParseType; @@ -554,8 +705,8 @@ type unused: array [0..4] of cint64; {$IFEND} - {$IF LIBAVFORMAT_VERSION >= 52006000} // 52.6.0 - filename: PChar; (**< source filename of the stream *) + {$IF (LIBAVFORMAT_VERSION >= 52006000) and (LIBAVFORMAT_VERSION_MAJOR < 53)} // 52.6.0 - 53.0.0 + filename: PAnsiChar; (**< source filename of the stream *) {$IFEND} {$IF LIBAVFORMAT_VERSION >= 52008000} // 52.8.0 @@ -576,6 +727,43 @@ type *) sample_aspect_ratio: TAVRational; {$IFEND} + + {$IF LIBAVFORMAT_VERSION >= 52024001} // 52.24.1 + metadata: PAVMetadata; + {$IFEND} + + {$IF LIBAVFORMAT_VERSION > 52024001} // > 52.24.1 + {* av_read_frame() support *} + cur_ptr: {const} PCuint8; + cur_len: cint; + cur_pkt: TAVPacket; + {$IFEND} + + {$IF LIBAVFORMAT_VERSION >= 52030000} // > 52.30.0 + // Timestamp generation support: + (** + * Timestamp corresponding to the last dts sync point. + * + * Initialized when AVCodecParserContext.dts_sync_point >= 0 and + * a DTS is received from the underlying container. Otherwise set to + * AV_NOPTS_VALUE by default. + *) + reference_dts: cint64; + {$IFEND} + {$IF LIBAVFORMAT_VERSION >= 52034000} // >= 52.34.0 + (** + * Number of packets to buffer for codec probing + * NOT PART OF PUBLIC API + *) + probe_packets: cint; + {$IFEND} + {$IF LIBAVFORMAT_VERSION >= 52038000} // >= 52.38.0 + (** + * last packet in packet_buffer for this stream when muxing. + * used internally, NOT PART OF PUBLIC API, dont read or write from outside of libav* + *) + last_in_packet_buffer: PAVPacketList; + {$IFEND} end; (** @@ -586,7 +774,7 @@ type * sizeof(AVFormatContext) must not be used outside libav*. *) TAVFormatContext = record - av_class: PAVClass; (**< Set by av_alloc_format_context. *) + av_class: PAVClass; (**< Set by avformat_alloc_context. *) (* Can only be iformat or oformat, not both at the same time. *) iformat: PAVInputFormat; oformat: PAVOutputFormat; @@ -600,17 +788,19 @@ type nb_streams: cuint; streams: array [0..MAX_STREAMS - 1] of PAVStream; - filename: array [0..1023] of char; (* input or output filename *) + filename: array [0..1023] of AnsiChar; (* input or output filename *) (* stream info *) timestamp: cint64; - title: array [0..511] of char; - author: array [0..511] of char; - copyright: array [0..511] of char; - comment: array [0..511] of char; - album: array [0..511] of char; + {$IF LIBAVFORMAT_VERSION < 53000000} // 53.00.0 + title: array [0..511] of AnsiChar; + author: array [0..511] of AnsiChar; + copyright: array [0..511] of AnsiChar; + comment: array [0..511] of AnsiChar; + album: array [0..511] of AnsiChar; year: cint; (**< ID3 year, 0 if none *) track: cint; (**< track number, 0 if none *) - genre: array [0..31] of char; (**< ID3 genre *) + genre: array [0..31] of AnsiChar; (**< ID3 genre *) + {$IFEND} ctx_flags: cint; (**< Format-specific flags, see AVFMTCTX_xx *) (* private data for pts handling (do not modify directly). *) @@ -636,16 +826,22 @@ type (* av_read_frame() support *) cur_st: PAVStream; - cur_ptr: pbyte; - cur_len: cint; - cur_pkt: TAVPacket; + {$IF LIBAVFORMAT_VERSION_MAJOR < 53} + cur_ptr_deprecated: pbyte; + cur_len_deprecated: cint; + cur_pkt_deprecated: TAVPacket; + {$IFEND} (* av_seek_frame() support *) data_offset: cint64; (* offset of the first packet *) index_built: cint; mux_rate: cint; + {$IF LIBAVFORMAT_VERSION < 52034001} // < 52.34.1 packet_size: cint; + {$ELSE} + packet_size: cuint; + {$IFEND} preload: cint; max_delay: cint; @@ -656,7 +852,7 @@ type loop_input: cint; {$IF LIBAVFORMAT_VERSION >= 50006000} // 50.6.0 - (** Decoding: size of data to probe; encoding: unused. *) + (** decoding: size of data to probe; encoding: unused. *) probesize: cuint; {$IFEND} @@ -696,8 +892,8 @@ type {$IF LIBAVFORMAT_VERSION >= 52004000} // 52.4.0 (** - * Maximum amount of memory in bytes to use per stream for the index. - * If the needed index exceeds this size, entries will be discarded as + * Maximum amount of memory in bytes to use for the index of each stream. + * If the index exceeds this size, entries will be discarded as * needed to maintain a smaller size. This can lead to slower or less * accurate seeking (depends on demuxer). * Demuxers for which a full in-memory index is mandatory will ignore @@ -740,6 +936,19 @@ type packet_buffer_end: PAVPacketList; {$IFEND} + + {$IF LIBAVFORMAT_VERSION >= 52024001} // 52.24.1 + metadata: PAVMetadata; + {$IFEND} + + {$IF LIBAVFORMAT_VERSION >= 52035000} // 52.35.0 + (** + * Remaining size available for raw_packet_buffer, in bytes. + * NOT PART OF PUBLIC API + *) + raw_packet_buffer_remaining_size: cint; + {$IFEND} + end; (** @@ -750,14 +959,19 @@ type *) TAVProgram = record id : cint; - provider_name : PChar; ///< network name for DVB streams - name : PChar; ///< service name for DVB streams + {$IF LIBAVFORMAT_VERSION < 53000000} // 53.00.0 + provider_name : PAnsiChar; ///< network name for DVB streams + name : PAnsiChar; ///< service name for DVB streams + {$IFEND} flags : cint; discard : TAVDiscard; ///< selects which program to discard and which to feed to the caller {$IF LIBAVFORMAT_VERSION >= 51016000} // 51.16.0 stream_index : PCardinal; nb_stream_indexes : PCardinal; {$IFEND} + {$IF LIBAVFORMAT_VERSION >= 52024001} // 52.24.1 + metadata: PAVMetadata; + {$IFEND} end; TAVPacketList = record @@ -779,8 +993,8 @@ type end; {deprecated} TAVImageFormat = record - name: pchar; - extensions: pchar; + name: PAnsiChar; + extensions: PAnsiChar; (* tell if a given file has a chance of being parsing by this format *) img_probe: function (d: PAVProbeData): cint; cdecl; (* read a whole image. 'alloc_cb' is called when the image size is @@ -801,10 +1015,10 @@ procedure av_register_image_format(img_fmt: PAVImageFormat); function av_probe_image_format(pd: PAVProbeData): PAVImageFormat; cdecl; external av__format; deprecated; -function guess_image_format(filename: pchar): PAVImageFormat; +function guess_image_format(filename: PAnsiChar): PAVImageFormat; cdecl; external av__format; deprecated; -function av_read_image(pb: PByteIOContext; filename: pchar; +function av_read_image(pb: PByteIOContext; filename: PAnsiChar; fmt: PAVImageFormat; alloc_cb: pointer; opaque: pointer): cint; cdecl; external av__format; deprecated; @@ -822,17 +1036,27 @@ var {$IFEND} {$IF LIBAVFORMAT_VERSION >= 52003000} // 52.3.0 +(** + * If f is NULL, returns the first registered input format, + * if f is non-NULL, returns the next registered input format after f + * or NULL if f is the last one. + *) function av_iformat_next(f: PAVInputFormat): PAVInputFormat; cdecl; external av__format; +(** + * If f is NULL, returns the first registered output format, + * if f is non-NULL, returns the next registered input format after f + * or NULL if f is the last one. + *) function av_oformat_next(f: PAVOutputFormat): PAVOutputFormat; cdecl; external av__format; {$IFEND} -function av_guess_image2_codec(filename: {const} PChar): TCodecID; +function av_guess_image2_codec(filename: {const} PAnsiChar): TCodecID; cdecl; external av__format; -(* XXX: use automatic init with either ELF sections or C file parser *) -(* modules *) +(* XXX: Use automatic init with either ELF sections or C file parser *) +(* modules. *) (* utils.c *) procedure av_register_input_format(format: PAVInputFormat); @@ -841,26 +1065,26 @@ procedure av_register_input_format(format: PAVInputFormat); procedure av_register_output_format(format: PAVOutputFormat); cdecl; external av__format; -function guess_stream_format(short_name: pchar; - filename: pchar; - mime_type: pchar): PAVOutputFormat; +function guess_stream_format(short_name: PAnsiChar; + filename: PAnsiChar; + mime_type: PAnsiChar): PAVOutputFormat; cdecl; external av__format; -function guess_format(short_name: pchar; - filename: pchar; - mime_type: pchar): PAVOutputFormat; +function guess_format(short_name: PAnsiChar; + filename: PAnsiChar; + mime_type: PAnsiChar): PAVOutputFormat; cdecl; external av__format; (** * Guesses the codec ID based upon muxer and filename. *) -function av_guess_codec(fmt: PAVOutputFormat; short_name: pchar; - filename: pchar; mime_type: pchar; +function av_guess_codec(fmt: PAVOutputFormat; short_name: PAnsiChar; + filename: PAnsiChar; mime_type: PAnsiChar; type_: TCodecType): TCodecID; cdecl; external av__format; (** - * Send a nice hexadecimal dump of a buffer to the specified file stream. + * Sends a nice hexadecimal dump of a buffer to the specified file stream. * * @param f The file stream pointer where the dump should be sent to. * @param buf buffer @@ -868,12 +1092,12 @@ function av_guess_codec(fmt: PAVOutputFormat; short_name: pchar; * * @see av_hex_dump_log, av_pkt_dump, av_pkt_dump_log *) -procedure av_hex_dump(f: PAVFile; buf: pchar; size: cint); +procedure av_hex_dump(f: PAVFile; buf: PByteArray; size: cint); cdecl; external av__format; {$IF LIBAVFORMAT_VERSION >= 51011000} // 51.11.0 (** - * Send a nice hexadecimal dump of a buffer to the log. + * Sends a nice hexadecimal dump of a buffer to the log. * * @param avcl A pointer to an arbitrary struct of which the first field is a * pointer to an AVClass struct. @@ -884,12 +1108,12 @@ procedure av_hex_dump(f: PAVFile; buf: pchar; size: cint); * * @see av_hex_dump, av_pkt_dump, av_pkt_dump_log *) -procedure av_hex_dump_log(avcl: Pointer; level: cint; buf: PChar; size: cint); +procedure av_hex_dump_log(avcl: Pointer; level: cint; buf: PByteArray; size: cint); cdecl; external av__format; {$IFEND} (** - * Send a nice dump of a packet to the specified file stream. + * Sends a nice dump of a packet to the specified file stream. * * @param f The file stream pointer where the dump should be sent to. * @param pkt packet to dump @@ -900,7 +1124,7 @@ procedure av_pkt_dump(f: PAVFile; pkt: PAVPacket; dump_payload: cint); {$IF LIBAVFORMAT_VERSION >= 51011000} // 51.11.0 (** - * Send a nice dump of a packet to the log. + * Sends a nice dump of a packet to the log. * * @param avcl A pointer to an arbitrary struct of which the first field is a * pointer to an AVClass struct. @@ -913,6 +1137,15 @@ procedure av_pkt_dump_log(avcl: Pointer; level: cint; pkt: PAVPacket; dump_paylo cdecl; external av__format; {$IFEND} +(** + * Initializes libavformat and registers all the muxers, demuxers and + * protocols. If you do not call this function, then you can select + * exactly which formats you want to support. + * + * @see av_register_input_format() + * @see av_register_output_format() + * @see av_register_protocol() + *) procedure av_register_all(); cdecl; external av__format; @@ -929,11 +1162,11 @@ function av_codec_get_tag(var tags: PAVCodecTag; id: TCodecID): cuint; (** * Finds AVInputFormat based on the short name of the input format. *) -function av_find_input_format(short_name: pchar): PAVInputFormat; +function av_find_input_format(short_name: PAnsiChar): PAVInputFormat; cdecl; external av__format; (** - * Guess file format. + * Guesses file format. * * @param is_opened Whether the file is already opened; determines whether * demuxers with or without AVFMT_NOFILE are probed. @@ -945,13 +1178,13 @@ function av_probe_input_format(pd: PAVProbeData; is_opened: cint): PAVInputForma * Allocates all the structures needed to read an input stream. * This does not open the needed codecs for decoding the stream[s]. *) -function av_open_input_stream(ic_ptr: PAVFormatContext; - pb: PByteIOContext; filename: pchar; +function av_open_input_stream(var ic_ptr: PAVFormatContext; + pb: PByteIOContext; filename: PAnsiChar; fmt: PAVInputFormat; ap: PAVFormatParameters): cint; cdecl; external av__format; (** - * Open a media file as input. The codecs are not opened. Only the file + * Opens a media file as input. The codecs are not opened. Only the file * header (if present) is read. * * @param ic_ptr The opened media file handle is put here. @@ -962,23 +1195,33 @@ function av_open_input_stream(ic_ptr: PAVFormatContext; * (NULL if default). * @return 0 if OK, AVERROR_xxx otherwise *) -function av_open_input_file(var ic_ptr: PAVFormatContext; filename: pchar; +function av_open_input_file(var ic_ptr: PAVFormatContext; filename: PAnsiChar; fmt: PAVInputFormat; buf_size: cint; ap: PAVFormatParameters): cint; cdecl; external av__format; +{$IF LIBAVFORMAT_VERSION >= 52026000} // 52.26.0 (** - * Allocate an AVFormatContext. + * Allocates an AVFormatContext. * Can be freed with av_free() but do not forget to free everything you * explicitly allocated as well! *) +function avformat_alloc_context(): PAVFormatContext; + cdecl; external av__format; +{$ELSE} + {$IF LIBAVFORMAT_VERSION_MAJOR < 53} +(** + * @deprecated Use avformat_alloc_context() instead. + *) function av_alloc_format_context(): PAVFormatContext; cdecl; external av__format; + {$IFEND} +{$IFEND} (** - * Read packets of a media file to get stream information. This + * Reads packets of a media file to get stream information. This * is useful for file formats with no headers such as MPEG. This - * function also computes the real frame rate in case of MPEG-2 repeat + * function also computes the real framerate in case of MPEG-2 repeat * frame mode. * The logical file position is not changed by this function; * examined packets may be buffered for later processing. @@ -992,7 +1235,7 @@ function av_find_stream_info(ic: PAVFormatContext): cint; cdecl; external av__format; (** - * Read a transport packet from a media file. + * Reads a transport packet from a media file. * * This function is obsolete and should never be used. * Use av_read_frame() instead. @@ -1005,7 +1248,7 @@ function av_read_packet(s: PAVFormatContext; var pkt: TAVPacket): cint; cdecl; external av__format; (** - * Return the next frame of a stream. + * Returns the next frame of a stream. * * The returned packet is valid * until the next av_read_frame() or until av_close_input_file() and @@ -1016,7 +1259,7 @@ function av_read_packet(s: PAVFormatContext; var pkt: TAVPacket): cint; * then it contains one frame. * * pkt->pts, pkt->dts and pkt->duration are always set to correct - * values in AVStream.timebase units (and guessed if the format cannot + * values in AVStream.time_base units (and guessed if the format cannot * provide them). pkt->pts can be AV_NOPTS_VALUE if the video format * has B-frames, so it is better to rely on pkt->dts if you do not * decompress the payload. @@ -1027,7 +1270,7 @@ function av_read_frame(s: PAVFormatContext; var pkt: TAVPacket): cint; cdecl; external av__format; (** - * Seek to the key frame at timestamp. + * Seeks to the keyframe at timestamp. * 'timestamp' in 'stream_index'. * @param stream_index If stream_index is (-1), a default * stream is selected, and timestamp is automatically converted @@ -1041,15 +1284,51 @@ function av_seek_frame(s: PAVFormatContext; stream_index: cint; timestamp: cint6 flags: cint): cint; cdecl; external av__format; +{$IF LIBAVFORMAT_VERSION >= 52026000} // 52.26.0 +(** + * Seeks to timestamp ts. + * Seeking will be done so that the point from which all active streams + * can be presented successfully will be closest to ts and within min/max_ts. + * Active streams are all streams that have AVStream.discard < AVDISCARD_ALL. + * + * If flags contain AVSEEK_FLAG_BYTE, then all timestamps are in byte and + * are the file position (this may not be supported by all demuxers). + * If flags contain AVSEEK_FLAG_FRAME then all timestamps are in frames + * in the stream with stream_index (this may not be supported by all demuxers). + * Otherwise all timestamps are in units of the stream selected by stream_index + * or if stream_index is -1, in AV_TIME_BASE units. + * If flags contain AVSEEK_FLAG_ANY, then non-keyframes are treated as + * keyframes (this may not be supported by all demuxers). + * + * @param stream_index index of the stream which is used as time base reference. + * @param min_ts smallest acceptable timestamp + * @param ts target timestamp + * @param max_ts largest acceptable timestamp + * @param flags flags + * @returns >=0 on success, error code otherwise + * + * @NOTE This is part of the new seek API which is still under construction. + * Thus do not use this yet. It may change at any time, do not expect + * ABI compatibility yet! + *) +function avformat_seek_file(s: PAVFormatContext; + stream_index: cint; + min_ts: cint64; + ts: cint64; + max_ts: cint64; + flags: cint): cint; + cdecl; external av__format; +{$IFEND} + (** - * Start playing a network based stream (e.g. RTSP stream) at the + * Starts playing a network-based stream (e.g. RTSP stream) at the * current position. *) function av_read_play(s: PAVFormatContext): cint; cdecl; external av__format; (** - * Pause a network based stream (e.g. RTSP stream). + * Pauses a network-based stream (e.g. RTSP stream). * * Use av_read_play() to resume it. *) @@ -1058,7 +1337,7 @@ function av_read_pause(s: PAVFormatContext): cint; {$IF LIBAVFORMAT_VERSION >= 52003000} // 52.3.0 (** - * Free a AVFormatContext allocated by av_open_input_stream. + * Frees a AVFormatContext allocated by av_open_input_stream. * @param s context to free *) procedure av_close_input_stream(s: PAVFormatContext); @@ -1066,7 +1345,7 @@ procedure av_close_input_stream(s: PAVFormatContext); {$IFEND} (** - * Close a media file (but not its codecs). + * Closes a media file (but not its codecs). * * @param s media file handle *) @@ -1074,7 +1353,7 @@ procedure av_close_input_file(s: PAVFormatContext); cdecl; external av__format; (** - * Add a new stream to a media file. + * Adds a new stream to a media file. * * Can only be called in the read_header() function. If the flag * AVFMTCTX_NOHEADER is in the format context, then new streams @@ -1092,7 +1371,7 @@ function av_new_program(s: PAVFormatContext; id: cint): PAVProgram; {$IF LIBAVFORMAT_VERSION >= 52014000} // 52.14.0 (** - * Add a new chapter. + * Adds a new chapter. * This function is NOT part of the public API * and should ONLY be used by demuxers. * @@ -1105,12 +1384,12 @@ function av_new_program(s: PAVFormatContext; id: cint): PAVProgram; * @return AVChapter or NULL on error *) function ff_new_chapter(s: PAVFormatContext; id: cint; time_base: TAVRational; - start, end_: cint64; title: {const} Pchar): PAVChapter; + start, end_: cint64; title: {const} PAnsiChar): PAVChapter; cdecl; external av__format; {$IFEND} (** - * Set the pts for a given stream. + * Sets the pts for a given stream. * * @param s stream * @param pts_wrap_bits number of bits effectively used by the pts @@ -1119,13 +1398,20 @@ function ff_new_chapter(s: PAVFormatContext; id: cint; time_base: TAVRational; * @param pts_den denominator to convert to seconds (MPEG: 90000) *) procedure av_set_pts_info(s: PAVStream; pts_wrap_bits: cint; +{$IF LIBAVFORMAT_VERSION < 52036000} // < 52.36.0 pts_num: cint; pts_den: cint); +{$ELSE} + pts_num: cuint; pts_den: cuint); +{$IFEND} cdecl; external av__format; const AVSEEK_FLAG_BACKWARD = 1; ///< seek backward AVSEEK_FLAG_BYTE = 2; ///< seeking based on position in bytes AVSEEK_FLAG_ANY = 4; ///< seek to any frame, even non-keyframes +{$IF LIBAVFORMAT_VERSION >= 52037000} // >= 52.37.0 + AVSEEK_FLAG_FRAME = 8; +{$IFEND} function av_find_default_stream_index(s: PAVFormatContext): cint; cdecl; external av__format; @@ -1144,7 +1430,7 @@ function av_index_search_timestamp(st: PAVStream; timestamp: cint64; flags: cint {$IF LIBAVFORMAT_VERSION >= 52004000} // 52.4.0 (** * Ensures the index uses less memory than the maximum specified in - * AVFormatContext.max_index_size, by discarding entries if it grows + * AVFormatContext.max_index_size by discarding entries if it grows * too large. * This function is not part of the public API and should only be called * by demuxers. @@ -1154,7 +1440,7 @@ procedure ff_reduce_index(s: PAVFormatContext; stream_index: cint); {$IFEND} (** - * Add an index entry into a sorted list. Update the entry if the list + * Adds an index entry into a sorted list. Updates the entry if the list * already contains it. * * @param timestamp timestamp in the timebase of the given stream @@ -1214,7 +1500,7 @@ function av_set_parameters(s: PAVFormatContext; ap: PAVFormatParameters): cint; cdecl; external av__format; (** - * Allocate the stream private data and write the stream header to an + * Allocates the stream private data and writes the stream header to an * output media file. * * @param s media file handle @@ -1224,7 +1510,7 @@ function av_write_header(s: PAVFormatContext): cint; cdecl; external av__format; (** - * Write a packet to an output media file. + * Writes a packet to an output media file. * * The packet shall contain one audio or video frame. * The packet must be correctly interleaved according to the container @@ -1242,7 +1528,7 @@ function av_write_frame(s: PAVFormatContext; var pkt: TAVPacket): cint; * Writes a packet to an output media file ensuring correct interleaving. * * The packet must contain one audio or video frame. - * If the packets are already correctly interleaved the application should + * If the packets are already correctly interleaved, the application should * call av_write_frame() instead as it is slightly faster. It is also important * to keep in mind that completely non-interleaved input will need huge amounts * of memory to interleave with this, so it is preferable to interleave at the @@ -1257,10 +1543,10 @@ function av_interleaved_write_frame(s: PAVFormatContext; var pkt: TAVPacket): ci cdecl; external av__format; (** - * Interleave a packet per dts in an output media file. + * Interleaves a packet per dts in an output media file. * * Packets with pkt->destruct == av_destruct_packet will be freed inside this - * function, so they cannot be used after it, note calling av_free_packet() + * function, so they cannot be used after it. Note that calling av_free_packet() * on them is still safe. * * @param s media file handle @@ -1275,9 +1561,27 @@ function av_interleave_packet_per_dts(s: PAVFormatContext; _out: PAVPacket; pkt: PAVPacket; flush: cint): cint; cdecl; external av__format; +{$IF LIBAVFORMAT_VERSION >= 52025000} // 52.25.0 (** - * @brief Write the stream trailer to an output media file and - * free the file private data. + * Add packet to AVFormatContext->packet_buffer list, determining its + * interleaved position using compare() function argument. + * + * This function is not part of the public API and should only be called + * by muxers using their own interleave function. + *) +{ +procedure ff_interleave_add_packet(s: PAVFormatContext; + pkt: PAVPacket; + compare: function(para1: PAVFormatContext; + para2: PAVPacket; + para3: PAVPacket): cint); + cdecl; external av__format; +} +{$IFEND} + +(** + * Writes the stream trailer to an output media file and frees the + * file private data. * * May only be called after a successful call to av_write_header. * @@ -1287,7 +1591,7 @@ function av_interleave_packet_per_dts(s: PAVFormatContext; _out: PAVPacket; function av_write_trailer(s: pAVFormatContext): cint; cdecl; external av__format; -procedure dump_format(ic: PAVFormatContext; index: cint; url: pchar; +procedure dump_format(ic: PAVFormatContext; index: cint; url: PAnsiChar; is_output: cint); cdecl; external av__format; @@ -1296,19 +1600,21 @@ procedure dump_format(ic: PAVFormatContext; index: cint; url: pchar; * @deprecated Use av_parse_video_frame_size instead. *) function parse_image_size(width_ptr: PCint; height_ptr: PCint; - str: pchar): cint; + str: PAnsiChar): cint; cdecl; external av__format; deprecated; +{$IF LIBAVFORMAT_VERSION_MAJOR < 53} (** - * Converts frame rate from string to a fraction. + * Converts framerate from a string to a fraction. * @deprecated Use av_parse_video_frame_rate instead. *) function parse_frame_rate(frame_rate: PCint; frame_rate_base: PCint; - arg: pchar): cint; + arg: PByteArray): cint; cdecl; external av__format; deprecated; +{$IFEND} (** - * Parses \p datestr and returns a corresponding number of microseconds. + * Parses datestr and returns a corresponding number of microseconds. * @param datestr String representing a date or a duration. * - If a date the syntax is: * @code @@ -1319,7 +1625,7 @@ function parse_frame_rate(frame_rate: PCint; frame_rate_base: PCint; * If the year-month-day part is not specified it takes the current * year-month-day. * Returns the number of microseconds since 1st of January, 1970 up to - * the time of the parsed date or INT64_MIN if \p datestr cannot be + * the time of the parsed date or INT64_MIN if datestr cannot be * successfully parsed. * - If a duration the syntax is: * @code @@ -1327,13 +1633,13 @@ function parse_frame_rate(frame_rate: PCint; frame_rate_base: PCint; * [-]S+[.m...] * @endcode * Returns the number of microseconds contained in a time interval - * with the specified duration or INT64_MIN if \p datestr cannot be + * with the specified duration or INT64_MIN if datestr cannot be * successfully parsed. - * @param duration Flag which tells how to interpret \p datestr, if - * not zero \p datestr is interpreted as a duration, otherwise as a + * @param duration Flag which tells how to interpret datestr, if + * not zero datestr is interpreted as a duration, otherwise as a * date. *) -function parse_date(datestr: pchar; duration: cint): cint64; +function parse_date(datestr: PAnsiChar; duration: cint): cint64; cdecl; external av__format; (** Gets the current time in microseconds. *) @@ -1347,7 +1653,11 @@ const function ffm_read_write_index(fd: cint): cint64; cdecl; external av__format; +{$IF LIBAVFORMAT_VERSION < 52027000} // 52.27.0 procedure ffm_write_write_index(fd: cint; pos: cint64); +{$ELSE} +function ffm_write_write_index(fd: cint; pos: cint64): cint; +{$IFEND} cdecl; external av__format; procedure ffm_set_write_index(s: PAVFormatContext; pos: cint64; file_size: cint64); @@ -1359,11 +1669,11 @@ procedure ffm_set_write_index(s: PAVFormatContext; pos: cint64; file_size: cint6 * syntax: '?tag1=val1&tag2=val2...'. Little URL decoding is done. * Return 1 if found. *) -function find_info_tag(arg: pchar; arg_size: cint; tag1: pchar; info: pchar): cint; +function find_info_tag(arg: PAnsiChar; arg_size: cint; tag1: PAnsiChar; info: PAnsiChar): cint; cdecl; external av__format; (** - * Returns in 'buf' the path with '%d' replaced by number. + * Returns in 'buf' the path with '%d' replaced by a number. * * Also handles the '%0nd' format where 'n' is the total number * of digits and '%%'. @@ -1374,20 +1684,20 @@ function find_info_tag(arg: pchar; arg_size: cint; tag1: pchar; info: pchar): ci * @param number frame number * @return 0 if OK, -1 on format error *) -function av_get_frame_filename(buf: pchar; buf_size: cint; - path: pchar; number: cint): cint; +function av_get_frame_filename(buf: PAnsiChar; buf_size: cint; + path: PAnsiChar; number: cint): cint; cdecl; external av__format {$IF LIBAVFORMAT_VERSION <= 50006000} // 50.6.0 name 'get_frame_filename' {$IFEND}; (** - * Check whether filename actually is a numbered sequence generator. + * Checks whether filename actually is a numbered sequence generator. * * @param filename possible numbered sequence string * @return 1 if a valid numbered sequence string, 0 otherwise *) -function av_filename_number_test(filename: pchar): cint; +function av_filename_number_test(filename: PAnsiChar): cint; cdecl; external av__format {$IF LIBAVFORMAT_VERSION <= 50006000} // 50.6.0 name 'filename_number_test' @@ -1395,7 +1705,7 @@ function av_filename_number_test(filename: pchar): cint; {$IF LIBAVFORMAT_VERSION >= 51012002} // 51.12.2 (** - * Generate an SDP for an RTP session. + * Generates an SDP for an RTP session. * * @param ac array of AVFormatContexts describing the RTP streams. If the * array is composed by only one context, such context can contain @@ -1408,7 +1718,7 @@ function av_filename_number_test(filename: pchar): cint; * @param size the size of the buffer * @return 0 if OK, AVERROR_xxx on error *) -function avf_sdp_create(ac: PPAVFormatContext; n_files: cint; buff: PChar; size: cint): cint; +function avf_sdp_create(ac: PPAVFormatContext; n_files: cint; buff: PByteArray; size: cint): cint; cdecl; external av__format; {$IFEND} @@ -1429,10 +1739,12 @@ begin end; {$IFEND} +{$IF LIBAVCODEC_VERSION < 52032000} // < 52.32.0 procedure av_free_packet(pkt: PAVPacket); begin if ((pkt <> nil) and (@pkt^.destruct <> nil)) then pkt^.destruct(pkt); end; +{$IFEND} end. diff --git a/Lua/src/lib/ffmpeg/avio.pas b/Lua/src/lib/ffmpeg/avio.pas index 5107a9fb..73c90b69 100644 --- a/Lua/src/lib/ffmpeg/avio.pas +++ b/Lua/src/lib/ffmpeg/avio.pas @@ -27,9 +27,20 @@ (* * Conversion of libavformat/avio.h - * revision 15120, Sun Aug 31 07:39:47 2008 UTC + * unbuffered I/O operations + * revision 16100, Sat Dec 13 13:39:13 2008 UTC + * update Tue, Jun 10 01:00:00 2009 UTC + * + * @warning This file has to be considered an internal but installed + * header, so it should not be directly included in your projects. *) +{ + * update to + * Max. avformat version: 52.41.0, Sun Dec 6 20:15:00 2009 CET + * MiSchi +} + unit avio; {$IFDEF FPC} @@ -48,13 +59,9 @@ uses ctypes, avutil, avcodec, + SysUtils, UConfig; -(* output byte stream handling *) - -type - TOffset = cint64; - (* unbuffered I/O *) const @@ -92,7 +99,7 @@ type is_streamed: cint; (**< true if streamed (no seek possible), default = false *) max_packet_size: cint; (**< if non zero, the stream is packetized with this max packet size *) priv_data: pointer; - filename: PChar; (**< specified filename *) + filename: PAnsiChar; (**< specified filename *) end; PPURLContext = ^PURLContext; @@ -104,23 +111,26 @@ type end; TURLProtocol = record - name: PChar; - url_open: function (h: PURLContext; filename: {const} PChar; flags: cint): cint; cdecl; - url_read: function (h: PURLContext; buf: PChar; size: cint): cint; cdecl; - url_write: function (h: PURLContext; buf: PChar; size: cint): cint; cdecl; - url_seek: function (h: PURLContext; pos: TOffset; whence: cint): TOffset; cdecl; + name: PAnsiChar; + url_open: function (h: PURLContext; filename: {const} PAnsiChar; flags: cint): cint; cdecl; + url_read: function (h: PURLContext; buf: PByteArray; size: cint): cint; cdecl; + {$IF LIBAVFORMAT_VERSION >= 52034001} // 52.34.1 + url_read_complete: function (h: PURLContext; buf: PByteArray; size: cint): cint; cdecl; + {$IFEND} + url_write: function (h: PURLContext; buf: PByteArray; size: cint): cint; cdecl; + url_seek: function (h: PURLContext; pos: cint64; whence: cint): cint64; cdecl; url_close: function (h: PURLContext): cint; cdecl; next: PURLProtocol; {$IF (LIBAVFORMAT_VERSION >= 52001000) and (LIBAVFORMAT_VERSION < 52004000)} // 52.1.0 .. 52.4.0 - url_read_play: function (h: PURLContext): cint; - url_read_pause: function (h: PURLContext): cint; + url_read_play: function (h: PURLContext): cint; cdecl; + url_read_pause: function (h: PURLContext): cint; cdecl; {$IFEND} {$IF LIBAVFORMAT_VERSION >= 52004000} // 52.4.0 url_read_pause: function (h: PURLContext; pause: cint): cint; cdecl; {$IFEND} {$IF LIBAVFORMAT_VERSION >= 52001000} // 52.1.0 - url_read_seek: function (h: PURLContext; - stream_index: cint; timestamp: cint64; flags: cint): TOffset; cdecl; + url_read_seek: function (h: PURLContext; stream_index: cint; + timestamp: cint64; flags: cint): cint64; cdecl; {$IFEND} end; @@ -133,23 +143,23 @@ type *) PByteIOContext = ^TByteIOContext; TByteIOContext = record - buffer: PChar; + buffer: PByteArray; buffer_size: cint; - buf_ptr: PChar; - buf_end: PChar; + buf_ptr: PByteArray; + buf_end: PByteArray; opaque: pointer; - read_packet: function (opaque: pointer; buf: PChar; buf_size: cint): cint; cdecl; - write_packet: function (opaque: pointer; buf: PChar; buf_size: cint): cint; cdecl; - seek: function (opaque: pointer; offset: TOffset; whence: cint): TOffset; cdecl; - pos: TOffset; (* position in the file of the current buffer *) + read_packet: function (opaque: pointer; buf: PByteArray; buf_size: cint): cint; cdecl; + write_packet: function (opaque: pointer; buf: PByteArray; buf_size: cint): cint; cdecl; + seek: function (opaque: pointer; offset: cint64; whence: cint): cint64; cdecl; + pos: cint64; (* position in the file of the current buffer *) must_flush: cint; (* true if the next seek should flush *) eof_reached: cint; (* true if eof reached *) write_flag: cint; (* true if open for writing *) is_streamed: cint; max_packet_size: cint; checksum: culong; - checksum_ptr: PCuchar; - update_checksum: function (checksum: culong; buf: {const} PChar; size: cuint): culong; cdecl; + checksum_ptr: PByteArray; + update_checksum: function (checksum: culong; buf: {const} PByteArray; size: cuint): culong; cdecl; error: cint; ///< contains the error code or 0 if no error happened {$IF (LIBAVFORMAT_VERSION >= 52001000) and (LIBAVFORMAT_VERSION < 52004000)} // 52.1.0 .. 52.4.0 read_play: function(opaque: Pointer): cint; cdecl; @@ -159,30 +169,40 @@ type read_pause: function(opaque: Pointer; pause: cint): cint; cdecl; {$IFEND} {$IF LIBAVFORMAT_VERSION >= 52001000} // 52.1.0 - read_seek: function(opaque: Pointer; - stream_index: cint; timestamp: cint64; flags: cint): TOffset; cdecl; + read_seek: function(opaque: Pointer; stream_index: cint; + timestamp: cint64; flags: cint): cint64; cdecl; {$IFEND} end; {$IF LIBAVFORMAT_VERSION >= 52021000} // 52.21.0 function url_open_protocol(puc: PPURLContext; up: PURLProtocol; - filename: {const} PChar; flags: cint): cint; + filename: {const} PAnsiChar; flags: cint): cint; cdecl; external av__format; {$IFEND} -function url_open(h: PPointer; filename: {const} PChar; flags: cint): cint; +function url_open(h: PPointer; filename: {const} PAnsiChar; flags: cint): cint; cdecl; external av__format; -function url_read (h: PURLContext; buf: PChar; size: cint): cint; +function url_read (h: PURLContext; buf: PByteArray; size: cint): cint; cdecl; external av__format; -function url_write (h: PURLContext; buf: PChar; size: cint): cint; +function url_write (h: PURLContext; buf: PByteArray; size: cint): cint; cdecl; external av__format; -function url_seek (h: PURLContext; pos: TOffset; whence: cint): TOffset; +function url_seek (h: PURLContext; pos: cint64; whence: cint): cint64; cdecl; external av__format; function url_close (h: PURLContext): cint; cdecl; external av__format; -function url_exist(filename: {const} PChar): cint; +function url_exist(filename: {const} PAnsiChar): cint; cdecl; external av__format; -function url_filesize (h: PURLContext): TOffset; +function url_filesize (h: PURLContext): cint64; + cdecl; external av__format; +{ + * Return the file descriptor associated with this URL. For RTP, this + * will return only the RTP file descriptor, not the RTCP file descriptor. + * To get both, use rtp_get_file_handles(). + * + * @return the file descriptor associated with this URL, or <0 on error. +} +(* not implemented *) +function url_get_file_handle(h: PURLContext): cint; cdecl; external av__format; (** @@ -195,7 +215,7 @@ function url_filesize (h: PURLContext): TOffset; *) function url_get_max_packet_size(h: PURLContext): cint; cdecl; external av__format; -procedure url_get_filename(h: PURLContext; buf: PChar; buf_size: cint); +procedure url_get_filename(h: PURLContext; buf: PAnsiChar; buf_size: cint); cdecl; external av__format; (** @@ -239,31 +259,49 @@ function av_url_read_pause(h: PURLContext; pause: cint): cint; * @return >= 0 on success * @see AVInputFormat::read_seek *) -function av_url_read_seek(h: PURLContext; - stream_index: cint; timestamp: cint64; flags: cint): TOffset; +function av_url_read_seek(h: PURLContext; stream_index: cint; + timestamp: cint64; flags: cint): cint64; cdecl; external av__format; {$IFEND} -{ +(** var +{$IF LIBAVFORMAT_VERSION_MAJOR < 53} first_protocol: PURLProtocol; external av__format; +{$IFEND} url_interrupt_cb: PURLInterruptCB; external av__format; -} +**) +{ +* If protocol is NULL, returns the first registered protocol, +* if protocol is non-NULL, returns the next registered protocol after protocol, +* or NULL if protocol is the last one. +} {$IF LIBAVFORMAT_VERSION >= 52002000} // 52.2.0 function av_protocol_next(p: PURLProtocol): PURLProtocol; cdecl; external av__format; {$IFEND} -function register_protocol (protocol: PURLProtocol): cint; +{$IF LIBAVFORMAT_VERSION <= 52028000} // 52.28.0 +(** + * @deprecated Use av_register_protocol() instead. + *) +function register_protocol(protocol: PURLProtocol): cint; cdecl; external av__format; +(** Alias for register_protocol() *) +function av_register_protocol(protocol: PURLProtocol): cint; + cdecl; external av__format name 'register_protocol'; +{$ELSE} +function av_register_protocol(protocol: PURLProtocol): cint; + cdecl; external av__format; +{$IFEND} type - TReadWriteFunc = function (opaque: Pointer; buf: PChar; buf_size: cint): cint; cdecl; - TSeekFunc = function (opaque: Pointer; offset: TOffset; whence: cint): TOffset; cdecl; + TReadWriteFunc = function(opaque: Pointer; buf: PByteArray; buf_size: cint): cint; cdecl; + TSeekFunc = function(opaque: Pointer; offset: cint64; whence: cint): cint64; cdecl; function init_put_byte(s: PByteIOContext; - buffer: PChar; + buffer: PByteArray; buffer_size: cint; write_flag: cint; opaque: pointer; read_packet: TReadWriteFunc; @@ -272,7 +310,7 @@ function init_put_byte(s: PByteIOContext; cdecl; external av__format; {$IF LIBAVFORMAT_VERSION >= 52004000} // 52.4.0 function av_alloc_put_byte( - buffer: PChar; + buffer: PByteArray; buffer_size: cint; write_flag: cint; opaque: Pointer; @@ -284,7 +322,7 @@ function av_alloc_put_byte( procedure put_byte(s: PByteIOContext; b: cint); cdecl; external av__format; -procedure put_buffer (s: PByteIOContext; buf: {const} PChar; size: cint); +procedure put_buffer (s: PByteIOContext; buf: {const} PByteArray; size: cint); cdecl; external av__format; procedure put_le64(s: PByteIOContext; val: cuint64); cdecl; external av__format; @@ -302,38 +340,38 @@ procedure put_le16(s: PByteIOContext; val: cuint); cdecl; external av__format; procedure put_be16(s: PByteIOContext; val: cuint); cdecl; external av__format; -procedure put_tag(s: PByteIOContext; tag: {const} PChar); +procedure put_tag(s: PByteIOContext; tag: {const} PAnsiChar); cdecl; external av__format; -procedure put_strz(s: PByteIOContext; buf: {const} PChar); +procedure put_strz(s: PByteIOContext; buf: {const} PAnsiChar); cdecl; external av__format; (** * fseek() equivalent for ByteIOContext. * @return new position or AVERROR. *) -function url_fseek(s: PByteIOContext; offset: TOffset; whence: cint): TOffset; +function url_fseek(s: PByteIOContext; offset: cint64; whence: cint): cint64; cdecl; external av__format; (** * Skip given number of bytes forward. * @param offset number of bytes *) -procedure url_fskip(s: PByteIOContext; offset: TOffset); +procedure url_fskip(s: PByteIOContext; offset: cint64); cdecl; external av__format; (** * ftell() equivalent for ByteIOContext. * @return position or AVERROR. *) -function url_ftell(s: PByteIOContext): TOffset; +function url_ftell(s: PByteIOContext): cint64; cdecl; external av__format; (** * Gets the filesize. * @return filesize or AVERROR *) -function url_fsize(s: PByteIOContext): TOffset; +function url_fsize(s: PByteIOContext): cint64; cdecl; external av__format; (** @@ -351,8 +389,8 @@ function av_url_read_fpause(h: PByteIOContext; pause: cint): cint; cdecl; external av__format; {$IFEND} {$IF LIBAVFORMAT_VERSION >= 52001000} // 52.1.0 -function av_url_read_fseek(h: PByteIOContext; - stream_index: cint; timestamp: cint64; flags: cint): TOffset; +function av_url_read_fseek(h: PByteIOContext; stream_index: cint; + timestamp: cint64; flags: cint): cint64; cdecl; external av__format; {$IFEND} @@ -363,12 +401,12 @@ function url_fgetc(s: PByteIOContext): cint; cdecl; external av__format; (** @warning currently size is limited *) -function url_fprintf(s: PByteIOContext; fmt: {const} PChar; args: array of const): cint; +function url_fprintf(s: PByteIOContext; fmt: {const} PAnsiChar; args: array of const): cint; cdecl; external av__format; (** @note unlike fgets, the EOL character is not returned and a whole line is parsed. return NULL if first char read was EOF *) -function url_fgets(s: PByteIOContext; buf: PChar; buf_size: cint): PChar; +function url_fgets(s: PByteIOContext; buf: PAnsiChar; buf_size: cint): PAnsiChar; cdecl; external av__format; procedure put_flush_packet (s: PByteIOContext); @@ -379,7 +417,7 @@ procedure put_flush_packet (s: PByteIOContext); * Reads size bytes from ByteIOContext into buf. * @returns number of bytes read or AVERROR *) -function get_buffer(s: PByteIOContext; buf: PChar; size: cint): cint; +function get_buffer(s: PByteIOContext; buf: PByteArray; size: cint): cint; cdecl; external av__format; (** @@ -388,7 +426,7 @@ function get_buffer(s: PByteIOContext; buf: PChar; size: cint): cint; * returned. * @returns number of bytes read or AVERROR *) -function get_partial_buffer(s: PByteIOContext; buf: PChar; size: cint): cint; +function get_partial_buffer(s: PByteIOContext; buf: PByteArray; size: cint): cint; cdecl; external av__format; (** @note return 0 if EOF, so you cannot use it if EOF handling is @@ -404,7 +442,7 @@ function get_le64(s: PByteIOContext): cuint64; function get_le16(s: PByteIOContext): cuint; cdecl; external av__format; -function get_strz(s: PByteIOContext; buf: PChar; maxlen: cint): PChar; +function get_strz(s: PByteIOContext; buf: PAnsiChar; maxlen: cint): PAnsiChar; cdecl; external av__format; function get_be16(s: PByteIOContext): cuint; cdecl; external av__format; @@ -435,6 +473,7 @@ function url_fdopen (s: PByteIOContext; h: PURLContext): cint; function url_setbufsize (s: PByteIOContext; buf_size: cint): cint; cdecl; external av__format; +{$IF LIBAVFORMAT_VERSION_MAJOR < 53} {$IF LIBAVFORMAT_VERSION >= 51015000} // 51.15.0 (** Reset the buffer for reading or writing. * @note Will drop any data currently in the buffer without transmitting it. @@ -443,13 +482,14 @@ function url_setbufsize (s: PByteIOContext; buf_size: cint): cint; function url_resetbuf(s: PByteIOContext; flags: cint): cint; cdecl; external av__format; {$IFEND} +{$IFEND} (** @note when opened as read/write, the buffers are only used for writing *) {$IF LIBAVFORMAT_VERSION >= 52000000} // 52.0.0 -function url_fopen(var s: PByteIOContext; filename: {const} PChar; flags: cint): cint; +function url_fopen(var s: PByteIOContext; filename: {const} PAnsiChar; flags: cint): cint; {$ELSE} -function url_fopen(s: PByteIOContext; filename: {const} PChar; flags: cint): cint; +function url_fopen(s: PByteIOContext; filename: {const} PAnsiChar; flags: cint): cint; {$IFEND} cdecl; external av__format; function url_fclose(s: PByteIOContext): cint; @@ -469,9 +509,9 @@ function url_fget_max_packet_size (s: PByteIOContext): cint; cdecl; external av__format; {$IF LIBAVFORMAT_VERSION >= 52000000} // 52.0.0 -function url_open_buf(var s: PByteIOContext; buf: PChar; buf_size: cint; flags: cint): cint; +function url_open_buf(var s: PByteIOContext; buf: PAnsiChar; buf_size: cint; flags: cint): cint; {$ELSE} -function url_open_buf(s: PByteIOContext; buf: PChar; buf_size: cint; flags: cint): cint; +function url_open_buf(s: PByteIOContext; buf: PAnsiChar; buf_size: cint; flags: cint): cint; {$IFEND} cdecl; external av__format; @@ -519,22 +559,27 @@ function url_close_dyn_buf(s: PByteIOContext; pbuffer:PPointer): cint; cdecl; external av__format; {$IF LIBAVFORMAT_VERSION >= 51017001} // 51.17.1 -function ff_crc04C11DB7_update(checksum: culong; buf: {const} PChar; len: cuint): culong; +function ff_crc04C11DB7_update(checksum: culong; buf: {const} PByteArray; + len: cuint): culong; cdecl; external av__format; {$IFEND} function get_checksum(s: PByteIOContext): culong; cdecl; external av__format; -procedure init_checksum (s: PByteIOContext; update_checksum: pointer; checksum: culong); +procedure init_gsum(s: PByteIOContext; + update_checksum: pointer; + checksum: culong); cdecl; external av__format; (* udp.c *) -function udp_set_remote_url(h: PURLContext; uri: {const} PChar): cint; +function udp_set_remote_url(h: PURLContext; uri: {const} PAnsiChar): cint; cdecl; external av__format; function udp_get_local_port(h: PURLContext): cint; cdecl; external av__format; +{$IF LIBAVFORMAT_VERSION_MAJOR <= 52} function udp_get_file_handle(h: PURLContext): cint; cdecl; external av__format; - +{$IFEND} + implementation function url_is_streamed(s: PByteIOContext): cint; diff --git a/Lua/src/lib/ffmpeg/avutil.pas b/Lua/src/lib/ffmpeg/avutil.pas index b4fae422..55bab601 100644 --- a/Lua/src/lib/ffmpeg/avutil.pas +++ b/Lua/src/lib/ffmpeg/avutil.pas @@ -29,14 +29,21 @@ * * libavutil/avutil.h: * Min. version: 49.0.1, revision 6577, Sat Oct 7 15:30:46 2006 UTC - * Max. version: 49.11.0, revision 15415, Thu Sep 25 19:23:13 2008 UTC + * Max. version: 49.14.0, revision 16912, Sun Feb 1 02:00:19 2009 UTC * * libavutil/mem.h: - * revision 15120, Sun Aug 31 07:39:47 2008 UTC + * revision 16590, Tue Jan 13 23:44:16 2009 UTC * * libavutil/log.h: - * revision 15120, Sun Aug 31 07:39:47 2008 UTC + * revision 16571, Tue Jan 13 00:14:43 2009 UTC *) +{ + Update changes auf avutil.h, mem.h and log.h + Max. version 50.05.1, Sun, Dec 6 24:00:00 2009 UTC + include/keep pixfmt.h (change in revision 50.01.0) + Maybe, the pixelformats are not needed, but it has not been checked. + log.h is only partial. +} unit avutil; @@ -62,9 +69,9 @@ uses const (* Max. supported version by this header *) - LIBAVUTIL_MAX_VERSION_MAJOR = 49; - LIBAVUTIL_MAX_VERSION_MINOR = 11; - LIBAVUTIL_MAX_VERSION_RELEASE = 0; + LIBAVUTIL_MAX_VERSION_MAJOR = 50; + LIBAVUTIL_MAX_VERSION_MINOR = 5; + LIBAVUTIL_MAX_VERSION_RELEASE = 1; LIBAVUTIL_MAX_VERSION = (LIBAVUTIL_MAX_VERSION_MAJOR * VERSION_MAJOR) + (LIBAVUTIL_MAX_VERSION_MINOR * VERSION_MINOR) + (LIBAVUTIL_MAX_VERSION_RELEASE * VERSION_RELEASE); @@ -94,88 +101,179 @@ function avutil_version(): cuint; cdecl; external av__format; {$IFEND} +{$IF LIBAVUTIL_VERSION >= 50004000} // >= 50.4.0 +(** + * Returns the libavutil build-time configuration. + *) +function avutil_configuration(): PAnsiChar; + cdecl; external av__format; + +(** + * Returns the libavutil license. + *) +function avutil_license(): PAnsiChar; + cdecl; external av__format; +{$IFEND} + type (** * Pixel format. Notes: * - * PIX_FMT_RGB32 is handled in an endian-specific manner. A RGBA + * PIX_FMT_RGB32 is handled in an endian-specific manner. An RGBA * color is put together as: * (A << 24) | (R << 16) | (G << 8) | B - * This is stored as BGRA on little endian CPU architectures and ARGB on - * big endian CPUs. + * This is stored as BGRA on little-endian CPU architectures and ARGB on + * big-endian CPUs. * * When the pixel format is palettized RGB (PIX_FMT_PAL8), the palettized * image data is stored in AVFrame.data[0]. The palette is transported in - * AVFrame.data[1] and, is 1024 bytes long (256 4-byte entries) and is + * AVFrame.data[1], is 1024 bytes long (256 4-byte entries) and is * formatted the same as in PIX_FMT_RGB32 described above (i.e., it is * also endian-specific). Note also that the individual RGB palette * components stored in AVFrame.data[1] should be in the range 0..255. * This is important as many custom PAL8 video codecs that were designed * to run on the IBM VGA graphics adapter use 6-bit palette components. + * + * For all the 8bit per pixel formats, an RGB32 palette is in data[1] like + * for pal8. This palette is filled in automatically by the function + * allocating the picture. + * + * Note, make sure that all newly added big endian formats have pix_fmt&1==1 + * and that all newly added little endian formats have pix_fmt&1==0 + * this allows simpler detection of big vs little endian. *) PAVPixelFormat = ^TAVPixelFormat; TAVPixelFormat = ( PIX_FMT_NONE= -1, - PIX_FMT_YUV420P, ///< Planar YUV 4:2:0, 12bpp, (1 Cr & Cb sample per 2x2 Y samples) - PIX_FMT_YUYV422, ///< Packed YUV 4:2:2, 16bpp, Y0 Cb Y1 Cr - PIX_FMT_RGB24, ///< Packed RGB 8:8:8, 24bpp, RGBRGB... - PIX_FMT_BGR24, ///< Packed RGB 8:8:8, 24bpp, BGRBGR... - PIX_FMT_YUV422P, ///< Planar YUV 4:2:2, 16bpp, (1 Cr & Cb sample per 2x1 Y samples) - PIX_FMT_YUV444P, ///< Planar YUV 4:4:4, 24bpp, (1 Cr & Cb sample per 1x1 Y samples) - PIX_FMT_RGB32, ///< Packed RGB 8:8:8, 32bpp, (msb)8A 8R 8G 8B(lsb), in cpu endianness - PIX_FMT_YUV410P, ///< Planar YUV 4:1:0, 9bpp, (1 Cr & Cb sample per 4x4 Y samples) - PIX_FMT_YUV411P, ///< Planar YUV 4:1:1, 12bpp, (1 Cr & Cb sample per 4x1 Y samples) - PIX_FMT_RGB565, ///< Packed RGB 5:6:5, 16bpp, (msb) 5R 6G 5B(lsb), in cpu endianness - PIX_FMT_RGB555, ///< Packed RGB 5:5:5, 16bpp, (msb)1A 5R 5G 5B(lsb), in cpu endianness most significant bit to 1 + PIX_FMT_YUV420P, ///< planar YUV 4:2:0, 12bpp, (1 Cr & Cb sample per 2x2 Y samples) + PIX_FMT_YUYV422, ///< packed YUV 4:2:2, 16bpp, Y0 Cb Y1 Cr + PIX_FMT_RGB24, ///< packed RGB 8:8:8, 24bpp, RGBRGB... + PIX_FMT_BGR24, ///< packed RGB 8:8:8, 24bpp, BGRBGR... + PIX_FMT_YUV422P, ///< planar YUV 4:2:2, 16bpp, (1 Cr & Cb sample per 2x1 Y samples) + PIX_FMT_YUV444P, ///< planar YUV 4:4:4, 24bpp, (1 Cr & Cb sample per 1x1 Y samples) +{$IF LIBAVUTIL_VERSION <= 50001000} // 50.01.0 + PIX_FMT_RGB32, ///< packed RGB 8:8:8, 32bpp, (msb)8A 8R 8G 8B(lsb), in CPU endianness +{$IFEND} + PIX_FMT_YUV410P, ///< planar YUV 4:1:0, 9bpp, (1 Cr & Cb sample per 4x4 Y samples) + PIX_FMT_YUV411P, ///< planar YUV 4:1:1, 12bpp, (1 Cr & Cb sample per 4x1 Y samples) +{$IF LIBAVUTIL_VERSION <= 50000000} // 50.00.0 + PIX_FMT_RGB565, ///< packed RGB 5:6:5, 16bpp, (msb) 5R 6G 5B(lsb), in CPU endianness + PIX_FMT_RGB555, ///< packed RGB 5:5:5, 16bpp, (msb)1A 5R 5G 5B(lsb), in CPU endianness, most significant bit to 0 +{$IFEND} PIX_FMT_GRAY8, ///< Y , 8bpp PIX_FMT_MONOWHITE, ///< Y , 1bpp, 0 is white, 1 is black PIX_FMT_MONOBLACK, ///< Y , 1bpp, 0 is black, 1 is white PIX_FMT_PAL8, ///< 8 bit with PIX_FMT_RGB32 palette - PIX_FMT_YUVJ420P, ///< Planar YUV 4:2:0, 12bpp, full scale (jpeg) - PIX_FMT_YUVJ422P, ///< Planar YUV 4:2:2, 16bpp, full scale (jpeg) - PIX_FMT_YUVJ444P, ///< Planar YUV 4:4:4, 24bpp, full scale (jpeg) - PIX_FMT_XVMC_MPEG2_MC,///< XVideo Motion Acceleration via common packet passing(xvmc_render.h) + PIX_FMT_YUVJ420P, ///< planar YUV 4:2:0, 12bpp, full scale (JPEG) + PIX_FMT_YUVJ422P, ///< planar YUV 4:2:2, 16bpp, full scale (JPEG) + PIX_FMT_YUVJ444P, ///< planar YUV 4:4:4, 24bpp, full scale (JPEG) + PIX_FMT_XVMC_MPEG2_MC,///< XVideo Motion Acceleration via common packet passing PIX_FMT_XVMC_MPEG2_IDCT, - PIX_FMT_UYVY422, ///< Packed YUV 4:2:2, 16bpp, Cb Y0 Cr Y1 - PIX_FMT_UYYVYY411, ///< Packed YUV 4:1:1, 12bpp, Cb Y0 Y1 Cr Y2 Y3 - PIX_FMT_BGR32, ///< Packed RGB 8:8:8, 32bpp, (msb)8A 8B 8G 8R(lsb), in cpu endianness - PIX_FMT_BGR565, ///< Packed RGB 5:6:5, 16bpp, (msb) 5B 6G 5R(lsb), in cpu endianness - PIX_FMT_BGR555, ///< Packed RGB 5:5:5, 16bpp, (msb)1A 5B 5G 5R(lsb), in cpu endianness most significant bit to 1 - PIX_FMT_BGR8, ///< Packed RGB 3:3:2, 8bpp, (msb)2B 3G 3R(lsb) - PIX_FMT_BGR4, ///< Packed RGB 1:2:1, 4bpp, (msb)1B 2G 1R(lsb) - PIX_FMT_BGR4_BYTE, ///< Packed RGB 1:2:1, 8bpp, (msb)1B 2G 1R(lsb) - PIX_FMT_RGB8, ///< Packed RGB 3:3:2, 8bpp, (msb)2R 3G 3B(lsb) - PIX_FMT_RGB4, ///< Packed RGB 1:2:1, 4bpp, (msb)1R 2G 1B(lsb) - PIX_FMT_RGB4_BYTE, ///< Packed RGB 1:2:1, 8bpp, (msb)1R 2G 1B(lsb) - PIX_FMT_NV12, ///< Planar YUV 4:2:0, 12bpp, 1 plane for Y and 1 for UV + PIX_FMT_UYVY422, ///< packed YUV 4:2:2, 16bpp, Cb Y0 Cr Y1 + PIX_FMT_UYYVYY411, ///< packed YUV 4:1:1, 12bpp, Cb Y0 Y1 Cr Y2 Y3 +{$IF LIBAVUTIL_VERSION <= 50001000} // 50.01.0 + PIX_FMT_BGR32, ///< packed RGB 8:8:8, 32bpp, (msb)8A 8B 8G 8R(lsb), in CPU endianness +{$IFEND} +{$IF LIBAVUTIL_VERSION <= 50000000} // 50.00.0 + PIX_FMT_BGR565, ///< packed RGB 5:6:5, 16bpp, (msb) 5B 6G 5R(lsb), in CPU endianness + PIX_FMT_BGR555, ///< packed RGB 5:5:5, 16bpp, (msb)1A 5B 5G 5R(lsb), in CPU endianness, most significant bit to 1 +{$IFEND} + PIX_FMT_BGR8, ///< packed RGB 3:3:2, 8bpp, (msb)2B 3G 3R(lsb) + PIX_FMT_BGR4, ///< packed RGB 1:2:1, 4bpp, (msb)1B 2G 1R(lsb) + PIX_FMT_BGR4_BYTE, ///< packed RGB 1:2:1, 8bpp, (msb)1B 2G 1R(lsb) + PIX_FMT_RGB8, ///< packed RGB 3:3:2, 8bpp, (msb)2R 3G 3B(lsb) + PIX_FMT_RGB4, ///< packed RGB 1:2:1, 4bpp, (msb)1R 2G 1B(lsb) + PIX_FMT_RGB4_BYTE, ///< packed RGB 1:2:1, 8bpp, (msb)1R 2G 1B(lsb) + PIX_FMT_NV12, ///< planar YUV 4:2:0, 12bpp, 1 plane for Y and 1 for UV PIX_FMT_NV21, ///< as above, but U and V bytes are swapped - - PIX_FMT_RGB32_1, ///< Packed RGB 8:8:8, 32bpp, (msb)8R 8G 8B 8A(lsb), in cpu endianness - PIX_FMT_BGR32_1, ///< Packed RGB 8:8:8, 32bpp, (msb)8B 8G 8R 8A(lsb), in cpu endianness - +{$IF LIBAVUTIL_VERSION <= 50001000} // 50.01.0 + PIX_FMT_RGB32_1, ///< packed RGB 8:8:8, 32bpp, (msb)8R 8G 8B 8A(lsb), in CPU endianness + PIX_FMT_BGR32_1, ///< packed RGB 8:8:8, 32bpp, (msb)8B 8G 8R 8A(lsb), in CPU endianness +{$ELSE} // 50.02.0 + PIX_FMT_ARGB, ///< packed ARGB 8:8:8:8, 32bpp, ARGBARGB... + PIX_FMT_RGBA, ///< packed RGBA 8:8:8:8, 32bpp, RGBARGBA... + PIX_FMT_ABGR, ///< packed ABGR 8:8:8:8, 32bpp, ABGRABGR... + PIX_FMT_BGRA, ///< packed BGRA 8:8:8:8, 32bpp, BGRABGRA... +{$IFEND} PIX_FMT_GRAY16BE, ///< Y , 16bpp, big-endian PIX_FMT_GRAY16LE, ///< Y , 16bpp, little-endian - PIX_FMT_YUV440P, ///< Planar YUV 4:4:0 (1 Cr & Cb sample per 1x2 Y samples) - PIX_FMT_YUVJ440P, ///< Planar YUV 4:4:0 full scale (jpeg) - PIX_FMT_YUVA420P, ///< Planar YUV 4:2:0, 20bpp, (1 Cr & Cb sample per 2x2 Y & A samples) + PIX_FMT_YUV440P, ///< planar YUV 4:4:0 (1 Cr & Cb sample per 1x2 Y samples) + PIX_FMT_YUVJ440P, ///< planar YUV 4:4:0 full scale (JPEG) + PIX_FMT_YUVA420P, ///< planar YUV 4:2:0, 20bpp, (1 Cr & Cb sample per 2x2 Y & A samples) + PIX_FMT_VDPAU_H264,///< H.264 HW decoding with VDPAU, data[0] contains a vdpau_render_state struct which contains the bitstream of the slices as well as various fields extracted from headers + PIX_FMT_VDPAU_MPEG1,///< MPEG-1 HW decoding with VDPAU, data[0] contains a vdpau_render_state struct which contains the bitstream of the slices as well as various fields extracted from headers + PIX_FMT_VDPAU_MPEG2,///< MPEG-2 HW decoding with VDPAU, data[0] contains a vdpau_render_state struct which contains the bitstream of the slices as well as various fields extracted from headers + PIX_FMT_VDPAU_WMV3,///< WMV3 HW decoding with VDPAU, data[0] contains a vdpau_render_state struct which contains the bitstream of the slices as well as various fields extracted from headers + PIX_FMT_VDPAU_VC1, ///< VC-1 HW decoding with VDPAU, data[0] contains a vdpau_render_state struct which contains the bitstream of the slices as well as various fields extracted from headers +{$IF LIBAVUTIL_VERSION >= 49015000} // 49.15.0 + PIX_FMT_RGB48BE, ///< packed RGB 16:16:16, 48bpp, 16R, 16G, 16B, big-endian + PIX_FMT_RGB48LE, ///< packed RGB 16:16:16, 48bpp, 16R, 16G, 16B, little-endian +{$IFEND} +{$IF LIBAVUTIL_VERSION >= 50001000} // 50.01.0 + PIX_FMT_RGB565BE, ///< packed RGB 5:6:5, 16bpp, (msb) 5R 6G 5B(lsb), big-endian + PIX_FMT_RGB565LE, ///< packed RGB 5:6:5, 16bpp, (msb) 5R 6G 5B(lsb), little-endian + PIX_FMT_RGB555BE, ///< packed RGB 5:5:5, 16bpp, (msb)1A 5R 5G 5B(lsb), big-endian, most significant bit to 0 + PIX_FMT_RGB555LE, ///< packed RGB 5:5:5, 16bpp, (msb)1A 5R 5G 5B(lsb), little-endian, most significant bit to 0 + + PIX_FMT_BGR565BE, ///< packed BGR 5:6:5, 16bpp, (msb) 5B 6G 5R(lsb), big-endian + PIX_FMT_BGR565LE, ///< packed BGR 5:6:5, 16bpp, (msb) 5B 6G 5R(lsb), little-endian + PIX_FMT_BGR555BE, ///< packed BGR 5:5:5, 16bpp, (msb)1A 5B 5G 5R(lsb), big-endian, most significant bit to 1 + PIX_FMT_BGR555LE, ///< packed BGR 5:5:5, 16bpp, (msb)1A 5B 5G 5R(lsb), little-endian, most significant bit to 1 + + PIX_FMT_VAAPI_MOCO, ///< HW acceleration through VA API at motion compensation entry-point, Picture.data[3] contains a vaapi_render_state struct which contains macroblocks as well as various fields extracted from headers + PIX_FMT_VAAPI_IDCT, ///< HW acceleration through VA API at IDCT entry-point, Picture.data[3] contains a vaapi_render_state struct which contains fields extracted from headers + PIX_FMT_VAAPI_VLD, ///< HW decoding through VA API, Picture.data[3] contains a vaapi_render_state struct which contains the bitstream of the slices as well as various fields extracted from headers +{$IFEND} PIX_FMT_NB ///< number of pixel formats, DO NOT USE THIS if you want to link with shared libav* because the number of formats might differ between versions ); const {$ifdef WORDS_BIGENDIAN} - PIX_FMT_RGBA = PIX_FMT_RGB32_1; - PIX_FMT_BGRA = PIX_FMT_BGR32_1; - PIX_FMT_ARGB = PIX_FMT_RGB32; - PIX_FMT_ABGR = PIX_FMT_BGR32; - PIX_FMT_GRAY16 = PIX_FMT_GRAY16BE; + {$IF LIBAVUTIL_VERSION <= 50001000} // 50.01.0 + PIX_FMT_RGBA = PIX_FMT_RGB32_1; + PIX_FMT_BGRA = PIX_FMT_BGR32_1; + PIX_FMT_ARGB = PIX_FMT_RGB32; + PIX_FMT_ABGR = PIX_FMT_BGR32; + {$ELSE} // 50.02.0 + PIX_FMT_RGB32 = PIX_FMT_ARGB; + PIX_FMT_RGB32_1 = PIX_FMT_RGBA; + PIX_FMT_BGR32 = PIX_FMT_ABGR; + PIX_FMT_BGR32_1 = PIX_FMT_BGRA; + {$IFEND} + PIX_FMT_GRAY16 = PIX_FMT_GRAY16BE; + {$IF LIBAVUTIL_VERSION >= 49015000} // 49.15.0 + PIX_FMT_RGB48 = PIX_FMT_RGB48BE; + {$IFEND} + {$IF LIBAVUTIL_VERSION >= 50001000} // 50.01.0 + PIX_FMT_RGB565 = PIX_FMT_RGB565BE; + PIX_FMT_RGB555 = PIX_FMT_RGB555BE; + PIX_FMT_BGR565 = PIX_FMT_BGR565BE; + PIX_FMT_BGR555 = PIX_FMT_BGR555BE + {$IFEND} {$else} - PIX_FMT_RGBA = PIX_FMT_BGR32; - PIX_FMT_BGRA = PIX_FMT_RGB32; - PIX_FMT_ARGB = PIX_FMT_BGR32_1; - PIX_FMT_ABGR = PIX_FMT_RGB32_1; - PIX_FMT_GRAY16 = PIX_FMT_GRAY16LE; -{$endif} + {$IF LIBAVUTIL_VERSION <= 50001000} // 50.01.0 + PIX_FMT_RGBA = PIX_FMT_BGR32; + PIX_FMT_BGRA = PIX_FMT_RGB32; + PIX_FMT_ARGB = PIX_FMT_BGR32_1; + PIX_FMT_ABGR = PIX_FMT_RGB32_1; + {$ELSE} // 50.02.0 + PIX_FMT_RGB32 = PIX_FMT_BGRA; + PIX_FMT_RGB32_1 = PIX_FMT_ABGR; + PIX_FMT_BGR32 = PIX_FMT_RGBA; + PIX_FMT_BGR32_1 = PIX_FMT_ARGB; + {$IFEND} + PIX_FMT_GRAY16 = PIX_FMT_GRAY16LE; + {$IF LIBAVUTIL_VERSION >= 49015000} // 49.15.0 + PIX_FMT_RGB48 = PIX_FMT_RGB48LE; + {$IFEND} + {$IF LIBAVUTIL_VERSION >= 50001000} // 50.01.0 + PIX_FMT_RGB565 = PIX_FMT_RGB565LE; + PIX_FMT_RGB555 = PIX_FMT_RGB555LE; + PIX_FMT_BGR565 = PIX_FMT_BGR565LE; + PIX_FMT_BGR555 = PIX_FMT_BGR555LE; + {$IFEND} +{$ENDIF} {$IF LIBAVUTIL_VERSION_MAJOR < 50} // 50.0.0 PIX_FMT_UYVY411 = PIX_FMT_UYYVYY411; @@ -183,40 +281,41 @@ const PIX_FMT_YUV422 = PIX_FMT_YUYV422; {$IFEND} -(* common.h *) +(* libavutil/common.h *) // until now MKTAG is all from common.h KMS 9/6/2009 -function MKTAG(a,b,c,d: char): integer; +function MKTAG(a, b, c, d: AnsiChar): integer; -(* mem.h *) +(* libavutil/mem.h *) +(* memory handling functions *) (** - * Allocate a block of \p size bytes with alignment suitable for all + * Allocates a block of size bytes with alignment suitable for all * memory accesses (including vectors if available on the CPU). * @param size Size in bytes for the memory block to be allocated. - * @return Pointer to the allocated block, NULL if it cannot allocate - * it. + * @return Pointer to the allocated block, NULL if the block cannot + * be allocated. * @see av_mallocz() *) function av_malloc(size: cuint): pointer; cdecl; external av__util; {av_malloc_attrib av_alloc_size(1)} (** - * Allocate or reallocate a block of memory. - * If \p ptr is NULL and \p size > 0, allocate a new block. If \p - * size is zero, free the memory block pointed by \p ptr. + * Allocates or reallocates a block of memory. + * If ptr is NULL and size > 0, allocates a new block. If + * size is zero, frees the memory block pointed to by ptr. * @param size Size in bytes for the memory block to be allocated or * reallocated. * @param ptr Pointer to a memory block already allocated with * av_malloc(z)() or av_realloc() or NULL. - * @return Pointer to a newly reallocated block or NULL if it cannot - * reallocate or the function is used to free the memory block. + * @return Pointer to a newly reallocated block or NULL if the block + * cannot be allocated or the function is used to free the memory block. * @see av_fast_realloc() *) function av_realloc(ptr: pointer; size: cuint): pointer; cdecl; external av__util; {av_alloc_size(2)} (** - * Free a memory block which has been allocated with av_malloc(z)() or + * Frees a memory block which has been allocated with av_malloc(z)() or * av_realloc(). * @param ptr Pointer to the memory block which should be freed. * @note ptr = NULL is explicitly allowed. @@ -227,29 +326,28 @@ procedure av_free(ptr: pointer); cdecl; external av__util; (** - * Allocate a block of \p size bytes with alignment suitable for all + * Allocates a block of size bytes with alignment suitable for all * memory accesses (including vectors if available on the CPU) and - * set to zeroes all the bytes of the block. + * zeroes all the bytes of the block. * @param size Size in bytes for the memory block to be allocated. - * @return Pointer to the allocated block, NULL if it cannot allocate - * it. + * @return Pointer to the allocated block, NULL if it cannot be allocated. * @see av_malloc() *) function av_mallocz(size: cuint): pointer; cdecl; external av__util; {av_malloc_attrib av_alloc_size(1)} (** - * Duplicate the string \p s. - * @param s String to be duplicated. + * Duplicates the string s. + * @param s string to be duplicated. * @return Pointer to a newly allocated string containing a - * copy of \p s or NULL if it cannot be allocated. + * copy of s or NULL if the string cannot be allocated. *) -function av_strdup({const} s: PChar): PChar; +function av_strdup({const} s: PAnsiChar): PAnsiChar; cdecl; external av__util; {av_malloc_attrib} (** - * Free a memory block which has been allocated with av_malloc(z)() or - * av_realloc() and set to NULL the pointer to it. + * Frees a memory block which has been allocated with av_malloc(z)() or + * av_realloc() and set the pointer pointing to it to NULL. * @param ptr Pointer to the pointer to the memory block which should * be freed. * @see av_free() @@ -257,7 +355,7 @@ function av_strdup({const} s: PChar): PChar; procedure av_freep (ptr: pointer); cdecl; external av__util; -(* log.h *) +(* libavutil/log.h *) const {$IF LIBAVUTIL_VERSION_MAJOR < 50} @@ -272,26 +370,26 @@ const AV_LOG_QUIET = -8; (** - * something went really wrong and we will crash now + * Something went really wrong and we will crash now. *) AV_LOG_PANIC = 0; (** - * something went wrong and recovery is not possible - * like no header in a format which depends on it or a combination - * of parameters which are not allowed + * Something went wrong and recovery is not possible. + * For example, no header was found for a format which depends + * on headers or an illegal combination of parameters is used. *) AV_LOG_FATAL = 8; (** - * something went wrong and cannot losslessly be recovered - * but not all future data is affected + * Something went wrong and cannot losslessly be recovered. + * However, not all future data is affected. *) AV_LOG_ERROR = 16; (** - * something somehow does not look correct / something which may or may not - * lead to some problems like use of -vstrict -2 + * Something somehow does not look correct. This may or may not + * lead to problems. An example would be the use of '-vstrict -2'. *) AV_LOG_WARNING = 24; @@ -299,7 +397,7 @@ const AV_LOG_VERBOSE = 40; (** - * stuff which is only useful for libav* developers + * Stuff which is only useful for libav* developers. *) AV_LOG_DEBUG = 48; {$IFEND} @@ -312,7 +410,9 @@ procedure av_log_set_level(level: cint); implementation -function MKTAG(a,b,c,d: char): integer; +(* libavutil/common.h *) + +function MKTAG(a, b, c, d: AnsiChar): integer; begin Result := (ord(a) or (ord(b) shl 8) or (ord(c) shl 16) or (ord(d) shl 24)); end; diff --git a/Lua/src/lib/ffmpeg/mathematics.pas b/Lua/src/lib/ffmpeg/mathematics.pas index 606d9189..f3a307b6 100644 --- a/Lua/src/lib/ffmpeg/mathematics.pas +++ b/Lua/src/lib/ffmpeg/mathematics.pas @@ -26,8 +26,16 @@ (* * Conversion of libavutil/mathematics.h - * revision 15120, Sun Aug 31 07:39:47 2008 UTC + * revision 16844, Wed Jan 28 08:50:10 2009 UTC + * + * update, MiSchi, no code change + * Fri Jun 12 2009 21:50:00 UTC *) +{ + * update to + * avutil max. version 50.05.1, Sun, Dec 6 24:00:00 2009 UTC + * MiSchi +} unit mathematics; @@ -52,32 +60,41 @@ const M_LN10 = 2.30258509299404568402; // log_e 10 M_PI = 3.14159265358979323846; // pi M_SQRT1_2 = 0.70710678118654752440; // 1/sqrt(2) +{$IF LIBAVUTIL_VERSION >= 50005001} // >= 50.5.1 + NAN = 0.0/0.0; + INFINITY = 1.0/0.0; +{$IFEND} type TAVRounding = ( - AV_ROUND_ZERO = 0, ///< round toward zero - AV_ROUND_INF = 1, ///< round away from zero - AV_ROUND_DOWN = 2, ///< round toward -infinity - AV_ROUND_UP = 3, ///< round toward +infinity - AV_ROUND_NEAR_INF = 5 ///< round to nearest and halfway cases away from zero + AV_ROUND_ZERO = 0, ///< Round toward zero. + AV_ROUND_INF = 1, ///< Round away from zero. + AV_ROUND_DOWN = 2, ///< Round toward -infinity. + AV_ROUND_UP = 3, ///< Round toward +infinity. + AV_ROUND_NEAR_INF = 5 ///< Round to nearest and halfway cases away from zero. ); +{$IF LIBAVUTIL_VERSION >= 49013000} // 49.13.0 +function av_gcd(a: cint64; b: cint64): cint64; + cdecl; external av__util; {av_const} +{$IFEND} + (** - * rescale a 64bit integer with rounding to nearest. - * a simple a*b/c isn't possible as it can overflow + * Rescales a 64-bit integer with rounding to nearest. + * A simple a*b/c isn't possible as it can overflow. *) function av_rescale (a, b, c: cint64): cint64; cdecl; external av__util; {av_const} (** - * rescale a 64bit integer with specified rounding. - * a simple a*b/c isn't possible as it can overflow + * Rescales a 64-bit integer with specified rounding. + * A simple a*b/c isn't possible as it can overflow. *) function av_rescale_rnd (a, b, c: cint64; enum: TAVRounding): cint64; cdecl; external av__util; {av_const} (** - * rescale a 64bit integer by 2 rational numbers. + * Rescales a 64-bit integer by 2 rational numbers. *) function av_rescale_q (a: cint64; bq, cq: TAVRational): cint64; cdecl; external av__util; {av_const} @@ -85,4 +102,3 @@ function av_rescale_q (a: cint64; bq, cq: TAVRational): cint64; implementation end. - diff --git a/Lua/src/lib/ffmpeg/opt.pas b/Lua/src/lib/ffmpeg/opt.pas index e734aa9f..65e055ce 100644 --- a/Lua/src/lib/ffmpeg/opt.pas +++ b/Lua/src/lib/ffmpeg/opt.pas @@ -27,8 +27,16 @@ (* * Conversion of libavcodec/opt.h - * revision 15120, Sun Aug 31 07:39:47 2008 UTC + * revision 16912, Sun Feb 1 02:00:19 2009 UTC + * + * update, MiSchi, no code change + * Fri Jun 12 2009 21:50:00 UTC *) +{ + * update to + * Max. version: 52.42.0, Sun Dec 6 19:20:00 2009 CET + * MiSchi +} unit opt; @@ -74,13 +82,13 @@ type *) PAVOption = ^TAVOption; TAVOption = record - name: {const} PChar; + name: {const} PAnsiChar; (** * short English help text * @todo What about other languages? *) - help: {const} PChar; + help: {const} PAnsiChar; (** * The offset relative to the context structure where the option @@ -104,34 +112,103 @@ type * options and corresponding named constants share the same * unit. May be NULL. *) - unit_: {const} PChar; + unit_: {const} PAnsiChar; end; +{$IF LIBAVCODEC_VERSION >= 52042000} // >= 52.42.0 +(** + * AVOption2. + * THIS IS NOT PART OF THE API/ABI YET! + * This is identical to AVOption except that default_val was replaced by + * an union, it should be compatible with AVOption on normal platforms. + *) +const + AV_OPT_FLAG_ENCODING_PARAM = 1; ///< a generic parameter which can be set by the user for muxing or encoding + AV_OPT_FLAG_DECODING_PARAM = 2; ///< a generic parameter which can be set by the user for demuxing or decoding + AV_OPT_FLAG_METADATA = 4; ///< some data extracted or inserted into the file like title, comment, ... + AV_OPT_FLAG_AUDIO_PARAM = 8; + AV_OPT_FLAG_VIDEO_PARAM = 16; + AV_OPT_FLAG_SUBTITLE_PARAM = 32; +type + PAVOption2 = ^TAVOption2; + TAVOption2 = record + name : {const} PAnsiChar; + + (** + * short English help text + * @todo What about other languages? + *) + help : {const} PAnsiChar; + + (** + * The offset relative to the context structure where the option + * value is stored. It should be 0 for named constants. + *) + offset : cint; + type_ : TAVOptionType; + + (** + * the default value for scalar options + *) + default_val : record + case cint of + 0 : (dbl: cdouble); + 1 : (str: PAnsiChar); + end; + min : cdouble; + max : cdouble; + flags : cint; +//FIXME think about enc-audio, ... style flags + + (** + * The logical unit to which the option belongs. Non-constant + * options and corresponding named constants share the same + * unit. May be NULL. + *) + unit_: {const} PAnsiChar; + end; +{$IFEND} + {$IF LIBAVCODEC_VERSION >= 51039000} // 51.39.0 (** - * Looks for an option in \p obj. Looks only for the options which - * have the flags set as specified in \p mask and \p flags (that is, + * Looks for an option in obj. Looks only for the options which + * have the flags set as specified in mask and flags (that is, * for which it is the case that opt->flags & mask == flags). * * @param[in] obj a pointer to a struct whose first element is a - * pointer to an #AVClass + * pointer to an AVClass * @param[in] name the name of the option to look for * @param[in] unit the unit of the option to look for, or any if NULL * @return a pointer to the option found, or NULL if no option * has been found *) -function av_find_opt(obj: Pointer; {const} name: {const} PChar; {const} unit_: PChar; mask: cint; flags: cint): {const} PAVOption; +function av_find_opt(obj: Pointer; {const} name: {const} PAnsiChar; {const} unit_: PAnsiChar; mask: cint; flags: cint): {const} PAVOption; cdecl; external av__codec; {$IFEND} +{$IF LIBAVCODEC_VERSION_MAJOR < 53} + (** * @see av_set_string2() *) -function av_set_string(obj: pointer; name: {const} pchar; val: {const} pchar): {const} PAVOption; +function av_set_string(obj: pointer; name: {const} PAnsiChar; val: {const} PAnsiChar): {const} PAVOption; cdecl; external av__codec; deprecated; {$IF LIBAVCODEC_VERSION >= 51059000} // 51.59.0 (** + * @return a pointer to the AVOption corresponding to the field set or + * NULL if no matching AVOption exists, or if the value val is not + * valid + * @see av_set_string3() + *) +function av_set_string2(obj: Pointer; name: {const} PAnsiChar; val: {const} PAnsiChar; alloc: cint): {const} PAVOption; + cdecl; external av__codec; deprecated; +{$IFEND} + +{$IFEND} + +{$IF LIBAVCODEC_VERSION >= 52007000} // 52.7.0 +(** * Sets the field of obj with the given name to value. * * @param[in] obj A struct whose first element is a pointer to an @@ -147,36 +224,40 @@ function av_set_string(obj: pointer; name: {const} pchar; val: {const} pchar): { * scalars or named flags separated by '+' or '-'. Prefixing a flag * with '+' causes it to be set without affecting the other flags; * similarly, '-' unsets a flag. - * @return a pointer to the AVOption corresponding to the field set or - * NULL if no matching AVOption exists, or if the value \p val is not - * valid + * @param[out] o_out if non-NULL put here a pointer to the AVOption + * found * @param alloc when 1 then the old value will be av_freed() and the * new av_strduped() * when 0 then no av_free() nor av_strdup() will be used + * @return 0 if the value has been set, or an AVERROR code in case of + * error: + * AVERROR(ENOENT) if no matching option exists + * AVERROR(ERANGE) if the value is out of range + * AVERROR(EINVAL) if the value is not valid *) -function av_set_string2(obj: Pointer; name: {const} PChar; val: {const} PChar; alloc: cint): {const} PAVOption; +function av_set_string3(obj: Pointer; name: {const} PAnsiChar; val: {const} PAnsiChar; alloc: cint; out o_out: {const} PAVOption): cint; cdecl; external av__codec; {$IFEND} -function av_set_double(obj: pointer; name: {const} pchar; n: cdouble): PAVOption; +function av_set_double(obj: pointer; name: {const} PAnsiChar; n: cdouble): PAVOption; cdecl; external av__codec; -function av_set_q(obj: pointer; name: {const} pchar; n: TAVRational): PAVOption; +function av_set_q(obj: pointer; name: {const} PAnsiChar; n: TAVRational): PAVOption; cdecl; external av__codec; -function av_set_int(obj: pointer; name: {const} pchar; n: cint64): PAVOption; +function av_set_int(obj: pointer; name: {const} PAnsiChar; n: cint64): PAVOption; cdecl; external av__codec; -function av_get_double(obj: pointer; name: {const} pchar; var o_out: PAVOption): cdouble; +function av_get_double(obj: pointer; name: {const} PAnsiChar; var o_out: PAVOption): cdouble; cdecl; external av__codec; -function av_get_q(obj: pointer; name: {const} pchar; var o_out: PAVOption): TAVRational; +function av_get_q(obj: pointer; name: {const} PAnsiChar; var o_out: PAVOption): TAVRational; cdecl; external av__codec; -function av_get_int(obj: pointer; name: {const} pchar; var o_out: {const} PAVOption): cint64; +function av_get_int(obj: pointer; name: {const} PAnsiChar; var o_out: {const} PAVOption): cint64; cdecl; external av__codec; -function av_get_string(obj: pointer; name: {const} pchar; var o_out: {const} PAVOption; buf: pchar; buf_len: cint): pchar; +function av_get_string(obj: pointer; name: {const} PAnsiChar; var o_out: {const} PAVOption; buf: PAnsiChar; buf_len: cint): PAnsiChar; cdecl; external av__codec; function av_next_option(obj: pointer; last: {const} PAVOption): PAVOption; diff --git a/Lua/src/lib/ffmpeg/rational.pas b/Lua/src/lib/ffmpeg/rational.pas index 02d594ff..4b8a2dc8 100644 --- a/Lua/src/lib/ffmpeg/rational.pas +++ b/Lua/src/lib/ffmpeg/rational.pas @@ -1,5 +1,5 @@ (* - * Rational numbers + * rational numbers * Copyright (c) 2003 Michael Niedermayer <michaelni@gmx.at> * * This library is free software; you can redistribute it and/or @@ -27,7 +27,13 @@ (* * Conversion of libavutil/rational.h - * revision 15415, Thu Sep 25 19:23:13 2008 UTC + * revision 16912, Sun Feb 1 02:00:19 2009 UTC + * + * update, MiSchi, no code change + * Fri Jun 12 2009 22:20:00 UTC + * + * update, MiSchi, no code change needed + * Sun Dec 6 2009 22:20:00 UTC *) unit rational; @@ -49,9 +55,9 @@ uses UConfig; type -(* - * Rational number num/den. - *) + (* + * rational number numerator/denominator + *) PAVRational = ^TAVRational; TAVRational = record num: cint; ///< numerator @@ -62,65 +68,65 @@ type PAVRationalArray = ^TAVRationalArray; (** - * Compare two rationals. + * Compares two rationals. * @param a first rational * @param b second rational - * @return 0 if a==b, 1 if a>b and -1 if a<b. + * @return 0 if a==b, 1 if a>b and -1 if a<b *) function av_cmp_q(a: TAVRational; b: TAVRational): cint; {$IFDEF HasInline}inline;{$ENDIF} (** - * Rational to double conversion. + * Converts rational to double. * @param a rational to convert * @return (double) a *) function av_q2d(a: TAVRational): cdouble; {$IFDEF HasInline}inline;{$ENDIF} (** - * Reduce a fraction. + * Reduces a fraction. * This is useful for framerate calculations. - * @param dst_nom destination numerator + * @param dst_num destination numerator * @param dst_den destination denominator - * @param nom source numerator + * @param num source numerator * @param den source denominator - * @param max the maximum allowed for dst_nom & dst_den + * @param max the maximum allowed for dst_num & dst_den * @return 1 if exact, 0 otherwise *) -function av_reduce(dst_nom: PCint; dst_den: PCint; nom: cint64; den: cint64; max: cint64): cint; +function av_reduce(dst_num: PCint; dst_den: PCint; num: cint64; den: cint64; max: cint64): cint; cdecl; external av__util; (** * Multiplies two rationals. - * @param b first rational. - * @param c second rational. - * @return b*c. + * @param b first rational + * @param c second rational + * @return b*c *) function av_mul_q(b: TAVRational; c: TAVRational): TAVRational; cdecl; external av__util; {av_const} (** * Divides one rational by another. - * @param b first rational. - * @param c second rational. - * @return b/c. + * @param b first rational + * @param c second rational + * @return b/c *) function av_div_q(b: TAVRational; c: TAVRational): TAVRational; cdecl; external av__util; {av_const} (** * Adds two rationals. - * @param b first rational. - * @param c second rational. - * @return b+c. + * @param b first rational + * @param c second rational + * @return b+c *) function av_add_q(b: TAVRational; c: TAVRational): TAVRational; cdecl; external av__util; {av_const} (** * Subtracts one rational from another. - * @param b first rational. - * @param c second rational. - * @return b-c. + * @param b first rational + * @param c second rational + * @return b-c *) function av_sub_q(b: TAVRational; c: TAVRational): TAVRational; cdecl; external av__util; {av_const} @@ -129,28 +135,26 @@ function av_sub_q(b: TAVRational; c: TAVRational): TAVRational; * Converts a double precision floating point number to a rational. * @param d double to convert * @param max the maximum allowed numerator and denominator - * @return (AVRational) d. + * @return (AVRational) d *) function av_d2q(d: cdouble; max: cint): TAVRational; cdecl; external av__util; {av_const} {$IF LIBAVUTIL_VERSION >= 49011000} // 49.11.0 - (** - * @return 1 if \q1 is nearer to \p q than \p q2, -1 if \p q2 is nearer - * than \p q1, 0 if they have the same distance. + * @return 1 if q1 is nearer to q than q2, -1 if q2 is nearer + * than q1, 0 if they have the same distance. *) function av_nearer_q(q, q1, q2: TAVRational): cint; cdecl; external av__util; (** - * Finds the nearest value in \p q_list to \p q. + * Finds the nearest value in q_list to q. * @param q_list an array of rationals terminated by {0, 0} * @return the index of the nearest value found in the array *) function av_find_nearest_q_idx(q: TAVRational; q_list: {const} PAVRationalArray): cint; cdecl; external av__util; - {$IFEND} implementation diff --git a/Lua/src/lib/ffmpeg/swscale.pas b/Lua/src/lib/ffmpeg/swscale.pas index 965659d9..595e16ba 100644 --- a/Lua/src/lib/ffmpeg/swscale.pas +++ b/Lua/src/lib/ffmpeg/swscale.pas @@ -25,6 +25,11 @@ * Conversion of libswscale/swscale.h * revision 27592, Fri Sep 12 21:46:53 2008 UTC *) +{ + * update to + * Max. version: 0.7.2, Sun Dec 6 22:20:00 2009 CET + * MiSchi +} unit swscale; @@ -45,13 +50,14 @@ interface uses ctypes, avutil, + avcodec, UConfig; const (* Max. supported version by this header *) LIBSWSCALE_MAX_VERSION_MAJOR = 0; - LIBSWSCALE_MAX_VERSION_MINOR = 6; - LIBSWSCALE_MAX_VERSION_RELEASE = 1; + LIBSWSCALE_MAX_VERSION_MINOR = 7; + LIBSWSCALE_MAX_VERSION_RELEASE = 2; LIBSWSCALE_MAX_VERSION = (LIBSWSCALE_MAX_VERSION_MAJOR * VERSION_MAJOR) + (LIBSWSCALE_MAX_VERSION_MINOR * VERSION_MINOR) + (LIBSWSCALE_MAX_VERSION_RELEASE * VERSION_RELEASE); @@ -77,8 +83,22 @@ function swscale_version(): cuint; cdecl; external sw__scale; {$IFEND} +{$IF LIBSWSCALE_VERSION >= 000007002} // 0.7.2 +(** + * Returns the libswscale build-time configuration. + *) +function swscale_configuration(): PAnsiChar; + cdecl; external sw__scale; + +(** + * Returns the libswscale license. + *) +function swscale_license(): PAnsiChar; + cdecl; external sw__scale; +{$IFEND} + const - {* values for the flags, the stuff on the command line is different *} + (* values for the flags, the stuff on the command line is different *) SWS_FAST_BILINEAR = 1; SWS_BILINEAR = 2; SWS_BICUBIC = 4; @@ -98,10 +118,10 @@ const SWS_PRINT_INFO = $1000; - //the following 3 flags are not completely implemented - //internal chrominace subsampling info + // the following 3 flags are not completely implemented + // internal chrominace subsampling info SWS_FULL_CHR_H_INT = $2000; - //input subsampling info + // input subsampling info SWS_FULL_CHR_H_INP = $4000; SWS_DIRECT_BGR = $8000; SWS_ACCURATE_RND = $40000; @@ -123,15 +143,14 @@ const SWS_CS_SMPTE240M = 7; SWS_CS_DEFAULT = 5; - type // when used for filters they must have an odd number of elements // coeffs cannot be shared between vectors PSwsVector = ^TSwsVector; TSwsVector = record - coeff: PCdouble; - length: cint; + coeff: PCdouble; // pointer to the list of coefficients + length: cint; // number of coefficients in the vector end; // vectors can be shared @@ -148,63 +167,187 @@ type {internal structure} end; - +(** + * Frees the swscaler context swsContext. + * If swsContext is NULL, then does nothing. + *) procedure sws_freeContext(swsContext: PSwsContext); cdecl; external sw__scale; +(** + * Allocates and returns a SwsContext. You need it to perform + * scaling/conversion operations using sws_scale(). + * + * @param srcW the width of the source image + * @param srcH the height of the source image + * @param srcFormat the source image format + * @param dstW the width of the destination image + * @param dstH the height of the destination image + * @param dstFormat the destination image format + * @param flags specify which algorithm and options to use for rescaling + * @return a pointer to an allocated context, or NULL in case of error + *) function sws_getContext(srcW: cint; srcH: cint; srcFormat: TAVPixelFormat; - dstW: cint; dstH: cint; dstFormat: TAVPixelFormat; flags: cint; - srcFilter: PSwsFilter; dstFilter: PSwsFilter; param: PCdouble): PSwsContext; + dstW: cint; dstH: cint; dstFormat: TAVPixelFormat; + flags: cint; srcFilter: PSwsFilter; + dstFilter: PSwsFilter; param: PCdouble): PSwsContext; cdecl; external sw__scale; -function sws_scale(context: PSwsContext; src: PPCuint8Array; srcStride: PCintArray; srcSliceY: cint; srcSliceH: cint; - dst: PPCuint8Array; dstStride: PCintArray): cint; + +(** + * Scales the image slice in srcSlice and puts the resulting scaled + * slice in the image in dst. A slice is a sequence of consecutive + * rows in an image. + * + * Slices have to be provided in sequential order, either in + * top-bottom or bottom-top order. If slices are provided in + * non-sequential order the behavior of the function is undefined. + * + * @param context the scaling context previously created with + * sws_getContext() + * @param srcSlice the array containing the pointers to the planes of + * the source slice + * @param srcStride the array containing the strides for each plane of + * the source image + * @param srcSliceY the position in the source image of the slice to + * process, that is the number (counted starting from + * zero) in the image of the first row of the slice + * @param srcSliceH the height of the source slice, that is the number + * of rows in the slice + * @param dst the array containing the pointers to the planes of + * the destination image + * @param dstStride the array containing the strides for each plane of + * the destination image + * @return the height of the output slice + *) +function sws_scale(context: PSwsContext; srcSlice: PPCuint8Array; srcStride: PCintArray; + srcSliceY: cint; srcSliceH: cint; dst: PPCuint8Array; dstStride: PCintArray): cint; cdecl; external sw__scale; -function sws_scale_ordered(context: PSwsContext; src: PPCuint8Array; srcStride: PCintArray; srcSliceY: cint; - srcSliceH: cint; dst: PPCuint8Array; dstStride: PCintArray): cint; + +{$IF LIBSWSCALE_VERSION_MAJOR < 1} +// deprecated. Use sws_scale() instead. +function sws_scale_ordered(context: PSwsContext; src: PPCuint8Array; srcStride: PCintArray; + srcSliceY: cint; srcSliceH: cint; dst: PPCuint8Array; dstStride: PCintArray): cint; cdecl; external sw__scale; deprecated; +{$IFEND} -function sws_setColorspaceDetails(c: PSwsContext; inv_table: PQuadCintArray; srcRange: cint; table: PQuadCintArray; dstRange: cint; +(** + * @param inv_table the yuv2rgb coefficients, normally ff_yuv2rgb_coeffs[x] + * @param fullRange if 1 then the luma range is 0..255 if 0 it is 16..235 + * @return -1 if not supported + *) +function sws_setColorspaceDetails(c: PSwsContext; inv_table: PQuadCintArray; + srcRange: cint; table: PQuadCintArray; dstRange: cint; brightness: cint; contrast: cint; saturation: cint): cint; cdecl; external sw__scale; -function sws_getColorspaceDetails(c: PSwsContext; var inv_table: PQuadCintArray; var srcRange: cint; var table: PQuadCintArray; var dstRange: cint; + +(** + * @return -1 if not supported + *) +function sws_getColorspaceDetails(c: PSwsContext; var inv_table: PQuadCintArray; + var srcRange: cint; var table: PQuadCintArray; var dstRange: cint; var brightness: cint; var contrast: cint; var saturation: cint): cint; cdecl; external sw__scale; + +(** + * Returns a normalized Gaussian curve used to filter stuff + * quality=3 is high quality, lower is lower quality. + *) function sws_getGaussianVec(variance: cdouble; quality: cdouble): PSwsVector; cdecl; external sw__scale; + +(** + * Allocates and returns a vector with length coefficients, all + * with the same value c. + *) function sws_getConstVec(c: cdouble; length: cint): PSwsVector; cdecl; external sw__scale; + +(** + * Allocates and returns a vector with just one coefficient, with + * value 1.0. + *) function sws_getIdentityVec: PSwsVector; cdecl; external sw__scale; + +(** + * Scales all the coefficients of a by the scalar value. + *) procedure sws_scaleVec(a: PSwsVector; scalar: cdouble); cdecl; external sw__scale; + +(** + * Scales all the coefficients of a so that their sum equals height. + *) procedure sws_normalizeVec(a: PSwsVector; height: cdouble); cdecl; external sw__scale; + procedure sws_convVec(a: PSwsVector; b: PSwsVector); cdecl; external sw__scale; + procedure sws_addVec(a: PSwsVector; b: PSwsVector); cdecl; external sw__scale; + procedure sws_subVec(a: PSwsVector; b: PSwsVector); cdecl; external sw__scale; + procedure sws_shiftVec(a: PSwsVector; shift: cint); cdecl; external sw__scale; + +(** + * Allocates and returns a clone of the vector a, that is a vector + * with the same coefficients as a. + *) function sws_cloneVec(a: PSwsVector): PSwsVector; cdecl; external sw__scale; +{$IF LIBSWSCALE_VERSION_MAJOR < 1} +// deprecated Use sws_printVec2() instead. + procedure sws_printVec(a: PSwsVector); + cdecl; external sw__scale; deprecated; +{$IFEND} + +{$IF LIBSWSCALE_VERSION >= 000007000} // >= 0.7.0 +(** + * Prints with av_log() a textual representation of the vector a + * if log_level <= av_log_level. + *) +procedure sws_printVec2(a: PSwsVector; + log_ctx: PAVClass; // PAVClass is declared in avcodec.pas + log_level: cint); cdecl; external sw__scale; +{$IFEND} + procedure sws_freeVec(a: PSwsVector); cdecl; external sw__scale; -function sws_getDefaultFilter(lumaGBlur: cfloat; chromaGBlur: cfloat; lumaSarpen: cfloat; chromaSharpen: cfloat; chromaHShift: cfloat; - chromaVShift: cfloat; verbose: cint): PSwsFilter; +function sws_getDefaultFilter(lumaGBlur: cfloat; chromaGBlur: cfloat; + lumaSharpen: cfloat; chromaSharpen: cfloat; + chromaHShift: cfloat; chromaVShift: cfloat; + verbose: cint): PSwsFilter; cdecl; external sw__scale; + procedure sws_freeFilter(filter: PSwsFilter); cdecl; external sw__scale; +(** + * Checks if context can be reused, otherwise reallocates a new + * one. + * + * If context is NULL, just calls sws_getContext() to get a new + * context. Otherwise, checks if the parameters are the ones already + * saved in context. If that is the case, returns the current + * context. Otherwise, frees context and gets a new context with + * the new parameters. + * + * Be warned that srcFilter and dstFilter are not checked, they + * are assumed to remain the same. + *) function sws_getCachedContext(context: PSwsContext; - srcW: cint; srcH: cint; srcFormat: cint; - dstW: cint; dstH: cint; dstFormat: cint; flags: cint; - srcFilter: PSwsFilter; dstFilter: PSwsFilter; param: PCdouble): PSwsContext; + srcW: cint; srcH: cint; srcFormat: TAVPixelFormat; + dstW: cint; dstH: cint; dstFormat: TAVPixelFormat; + flags: cint; srcFilter: PSwsFilter; + dstFilter: PSwsFilter; param: PCdouble): PSwsContext; cdecl; external sw__scale; implementation diff --git a/Lua/src/lib/fft/UFFT.pas b/Lua/src/lib/fft/UFFT.pas index 6b094c98..5a056a8c 100644 --- a/Lua/src/lib/fft/UFFT.pas +++ b/Lua/src/lib/fft/UFFT.pas @@ -47,7 +47,7 @@ unit UFFT; {$IFDEF FPC} {$MODE Delphi} - {$H+} // Use AnsiString + {$H+} // Use long strings {$ENDIF} interface diff --git a/Lua/src/lib/freetype/demo/engine-test.bdsproj b/Lua/src/lib/freetype/demo/engine-test.bdsproj index 9547f18f..e5b3e97d 100644 --- a/Lua/src/lib/freetype/demo/engine-test.bdsproj +++ b/Lua/src/lib/freetype/demo/engine-test.bdsproj @@ -27,13 +27,13 @@ <Compiler Name="I">1</Compiler>
<Compiler Name="J">0</Compiler>
<Compiler Name="K">0</Compiler>
- <Compiler Name="L">1</Compiler>
- <Compiler Name="M">0</Compiler>
- <Compiler Name="N">1</Compiler>
- <Compiler Name="O">0</Compiler>
- <Compiler Name="P">1</Compiler>
- <Compiler Name="Q">0</Compiler>
- <Compiler Name="R">0</Compiler>
+ <Compiler Name="L">1</Compiler> + <Compiler Name="M">0</Compiler> + <Compiler Name="N">1</Compiler> + <Compiler Name="O">1</Compiler> + <Compiler Name="P">1</Compiler> + <Compiler Name="Q">0</Compiler> + <Compiler Name="R">0</Compiler> <Compiler Name="S">0</Compiler>
<Compiler Name="T">0</Compiler>
<Compiler Name="U">0</Compiler>
diff --git a/Lua/src/lib/freetype/demo/engine-test.dpr b/Lua/src/lib/freetype/demo/engine-test.dpr index 80177735..bbd7d890 100644 --- a/Lua/src/lib/freetype/demo/engine-test.dpr +++ b/Lua/src/lib/freetype/demo/engine-test.dpr @@ -27,7 +27,9 @@ uses ctypes in '../../ctypes/ctypes.pas', {$ENDIF} FreeType in '../freetype.pas', - UFont in '../../../base/UFont.pas', + UFont in 'UFont.pas', + //UFont in '../../../base/UFont.pas', + UUnicodeUtils in '../../../base/UUnicodeUtils.pas', math, sysutils; @@ -41,7 +43,7 @@ const //FONT_FILE = 'C:/Windows/Fonts/Arial.ttf'; //FONT_FILE = 'C:/Windows/Fonts/SimSun.ttf'; //FONT_FILE = 'eurostarregularextended.ttf'; - FONT_FILE = 'FreeSans.ttf'; + FONT_FILE = '../../../../game/fonts/FreeSans/FreeSans.ttf'; var OurFont: TScalableFont; @@ -129,11 +131,11 @@ begin // Really Nice Perspective Calculations glHint( GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST ); - //OurFont := TFTScalableFont.Create(FONT_FILE, 64); + OurFont := TFTScalableFont.Create(FONT_FILE, 64, 0.03); //OurFont := TFTFont.Create(FONT_FILE, 128); - OurFont := TFTScalableOutlineFont.Create(FONT_FILE, 64, 0.05); + //OurFont := TFTScalableOutlineFont.Create(FONT_FILE, 64, 0.03); //OurFont.UseKerning := false; - TFTScalableOutlineFont(OurFont).SetOutlineColor(1, 0, 0); + //TFTScalableOutlineFont(OurFont).SetOutlineColor(1, 0, 0, 1); //OurFont := TOutlineFont.Create(FONT_FILE, 32, 2); //OurFont.LineSpacing := OurFont.LineSpacing * 0.5; @@ -183,7 +185,7 @@ begin //OurFont.SetOutlineColor(0.5, 0.5, 0.5); //OurFont.ReflectionSpacing := -4; //OurFont.UseKerning := false; - OurFont.Height := 64;//cnt2; + OurFont.Height := 150;//cnt2; //OurFont.Reset; //OurFont.Aspect := 2; @@ -191,7 +193,7 @@ begin bounds := OurFont.BBox(msg); //glRectf(bounds.Left, OurFont.Ascender, bounds.Right, OurFont.Ascender-OurFont.Height); - glColor3f(1, 1, 1); + glColor4f(1, 1, 1, 1); //OurFont.ReflectionSpacing := 0; OurFont.Print(msg); diff --git a/Lua/src/lib/freetype/demo/engine-test.lpi b/Lua/src/lib/freetype/demo/engine-test.lpi index 6cbfe1eb..45483a56 100644 --- a/Lua/src/lib/freetype/demo/engine-test.lpi +++ b/Lua/src/lib/freetype/demo/engine-test.lpi @@ -28,14 +28,14 @@ </local>
</RunParams>
<Units Count="16">
- <Unit0>
- <Filename Value="engine-test.dpr"/>
- <IsPartOfProject Value="True"/>
- <CursorPos X="25" Y="135"/>
- <TopLine Value="118"/>
- <EditorIndex Value="0"/>
- <UsageCount Value="72"/>
- <Loaded Value="True"/>
+ <Unit0> + <Filename Value="engine-test.dpr"/> + <IsPartOfProject Value="True"/> + <CursorPos X="18" Y="25"/> + <TopLine Value="1"/> + <EditorIndex Value="0"/> + <UsageCount Value="72"/> + <Loaded Value="True"/> </Unit0>
<Unit1>
<Filename Value="JEDI-SDL\OpenGL\Pas\opengl12.pas"/>
@@ -139,13 +139,22 @@ <UnitName Value="UFont"/>
<CursorPos X="15" Y="1752"/>
<TopLine Value="1734"/>
- <UsageCount Value="10"/>
- </Unit15>
- </Units>
- <JumpHistory Count="0" HistoryIndex="-1"/>
- </ProjectOptions>
- <CompilerOptions>
- <Version Value="8"/>
+ <UsageCount Value="10"/> + </Unit15> + </Units> + <JumpHistory Count="2" HistoryIndex="1"> + <Position1> + <Filename Value="engine-test.dpr"/> + <Caret Line="52" Column="10" TopLine="37"/> + </Position1> + <Position2> + <Filename Value="engine-test.dpr"/> + <Caret Line="1" Column="1" TopLine="1"/> + </Position2> + </JumpHistory> + </ProjectOptions> + <CompilerOptions> + <Version Value="8"/> <PathDelim Value="\"/>
<SearchPaths>
<IncludeFiles Value="..\..\JEDI-SDL\SDL\Pas\"/>
diff --git a/Lua/src/lib/freetype/freetype.pas b/Lua/src/lib/freetype/freetype.pas index 6a9d2062..6aaa3b59 100644 --- a/Lua/src/lib/freetype/freetype.pas +++ b/Lua/src/lib/freetype/freetype.pas @@ -1,23 +1,42 @@ -//---------------------------------------------------------------------------- -// FreeType2 pascal header -//---------------------------------------------------------------------------- -// Anti-Grain Geometry - Version 2.4 (Public License) -// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com) -// -// Anti-Grain Geometry - Version 2.4 Release Milano 3 (AggPas 2.4 RM3) -// Pascal Port By: Milan Marusinec alias Milano -// milan@marusinec.sk -// http://www.aggpas.org -// Copyright (c) 2005-2007 -// -// Permission to copy, use, modify, sell and distribute this software -// is granted provided this copyright notice appears in all copies. -// This software is provided "as is" without express or implied -// warranty, and with no claim as to its suitability for any purpose. -// -//---------------------------------------------------------------------------- -// Adapted by the UltraStar Deluxe Team -//---------------------------------------------------------------------------- +(***************************************************************************) +(* *) +(* freetype.h *) +(* *) +(* FreeType high-level API and common types (specification only). *) +(* *) +(* Copyright 1996-2001, 2002, 2003, 2004, 2005, 2006, 2007 by *) +(* David Turner, Robert Wilhelm, and Werner Lemberg. *) +(* *) +(* This file is part of the FreeType project, and may only be used, *) +(* modified, and distributed under the terms of the FreeType project *) +(* license, LICENSE.TXT. By continuing to use, modify, or distribute *) +(* this file you indicate that you have read the license and *) +(* understand and accept it fully. *) +(* *) +(***************************************************************************) + +(***************************************************************************) +(* Initial Pascal port by *) +(***************************************************************************) +(* Anti-Grain Geometry - Version 2.4 (Public License) *) +(* Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com) *) +(* *) +(* Anti-Grain Geometry - Version 2.4 Release Milano 3 (AggPas 2.4 RM3) *) +(* Pascal Port By: Milan Marusinec alias Milano *) +(* milan@marusinec.sk *) +(* http://www.aggpas.org *) +(* Copyright (c) 2005-2007 *) +(* *) +(* Permission to copy, use, modify, sell and distribute this software *) +(* is granted provided this copyright notice appears in all copies. *) +(* This software is provided "as is" without express or implied *) +(* warranty, and with no claim as to its suitability for any purpose. *) +(* *) +(***************************************************************************) + +(***************************************************************************) +(* Extended by the UltraStar Deluxe Team *) +(***************************************************************************) unit freetype; @@ -36,7 +55,7 @@ uses const {$IF Defined(MSWINDOWS)} - ft_lib = 'libfreetype-6.dll'; + ft_lib = 'freetype6.dll'; {$ELSEIF Defined(DARWIN)} ft_lib = 'libfreetype.dylib'; {$LINKLIB libfreetype} @@ -45,32 +64,24 @@ const {$IFEND} type - FT_Byte = cuchar; - FT_Short = csshort; - FT_UShort = cushort; - FT_Int = csint; - FT_UInt = cuint; - FT_Int16 = cint16; - FT_UInt16 = cuint16; - FT_Int32 = cint32; - FT_UInt32 = cuint32; - FT_Long = cslong; - FT_ULong = culong; - - FT_Fixed = cslong; - FT_Pos = cslong; - FT_Error = cint; - FT_F26Dot6 = cslong; - FT_String = cchar; - FT_Bool = cuchar; - - PFT_Byte = ^FT_Byte; - PFT_Short = ^FT_Short; - PFT_String = ^FT_String; - - - TByteArray = array [0 .. (MaxInt div SizeOf(byte))-1] of byte; - PByteArray = ^TByteArray; + (*************************************************************************) + (* *) + (* <Type> *) + (* FT_Library *) + (* *) + (* <Description> *) + (* A handle to a FreeType library instance. Each `library' is *) + (* completely independent from the others; it is the `root' of a set *) + (* of objects like fonts, faces, sizes, etc. *) + (* *) + (* It also embeds a memory manager (see @FT_Memory), as well as a *) + (* scan-line converter object (see @FT_Raster). *) + (* *) + (* <Note> *) + (* Library objects are normally created by @FT_Init_FreeType, and *) + (* destroyed with @FT_Done_FreeType. *) + (* *) + FT_Library = Pointer; (*************************************************************************) @@ -290,54 +301,6 @@ const FT_ENCODING_WANSUNG: FT_Encoding = ('w', 'a', 'n', 's'); FT_ENCODING_JOHAB: FT_Encoding = ('j', 'o', 'h', 'a'); - (*************************************************************************) - (* *) - (* <Enum> *) - (* FT_Glyph_Format *) - (* *) - (* <Description> *) - (* An enumeration type used to describe the format of a given glyph *) - (* image. Note that this version of FreeType only supports two image *) - (* formats, even though future font drivers will be able to register *) - (* their own format. *) - (* *) - (* <Values> *) - (* FT_GLYPH_FORMAT_NONE :: *) - (* The value 0 is reserved and does describe a glyph format. *) - (* *) - (* FT_GLYPH_FORMAT_COMPOSITE :: *) - (* The glyph image is a composite of several other images. This *) - (* format is _only_ used with @FT_LOAD_NO_RECURSE, and is used to *) - (* report compound glyphs (like accented characters). *) - (* *) - (* FT_GLYPH_FORMAT_BITMAP :: *) - (* The glyph image is a bitmap, and can be described as an *) - (* @FT_Bitmap. You generally need to access the `bitmap' field of *) - (* the @FT_GlyphSlotRec structure to read it. *) - (* *) - (* FT_GLYPH_FORMAT_OUTLINE :: *) - (* The glyph image is a vertorial outline made of line segments *) - (* and Bezier arcs; it can be described as an @FT_Outline; you *) - (* generally want to access the `outline' field of the *) - (* @FT_GlyphSlotRec structure to read it. *) - (* *) - (* FT_GLYPH_FORMAT_PLOTTER :: *) - (* The glyph image is a vectorial path with no inside/outside *) - (* contours. Some Type 1 fonts, like those in the Hershey family, *) - (* contain glyphs in this format. These are described as *) - (* @FT_Outline, but FreeType isn't currently capable of rendering *) - (* them correctly. *) - (* *) -type - FT_Glyph_Format = array[0..3] of char; -const - FT_GLYPH_FORMAT_NONE: FT_Glyph_Format = (#0, #0, #0, #0 ); - - FT_GLYPH_FORMAT_COMPOSITE: FT_Glyph_Format = ('c', 'o', 'm', 'p' ); - FT_GLYPH_FORMAT_BITMAP: FT_Glyph_Format = ('b', 'i', 't', 's' ); - FT_GLYPH_FORMAT_OUTLINE: FT_Glyph_Format = ('o', 'u', 't', 'l' ); - FT_GLYPH_FORMAT_PLOTTER: FT_Glyph_Format = ('p', 'l', 'o', 't' ); - (*************************************************************************) (* *) @@ -358,7 +321,7 @@ const const FT_STYLE_FLAG_ITALIC = 1 shl 0; FT_STYLE_FLAG_BOLD = 1 shl 1; - + (*************************************************************************** * @@ -567,7 +530,7 @@ const (* perform this pass. *) (* *) type - FT_Render_Mode = FT_Int; + FT_Render_Mode = cint; const FT_RENDER_MODE_NORMAL = 0; FT_RENDER_MODE_LIGHT = FT_RENDER_MODE_NORMAL + 1; @@ -579,63 +542,34 @@ const (*************************************************************************) (* *) - (* <Enum> *) - (* FT_Pixel_Mode *) + (* <Type> *) + (* FT_GlyphSlot *) (* *) (* <Description> *) - (* An enumeration type used to describe the format of pixels in a *) - (* given bitmap. Note that additional formats may be added in the *) - (* future. *) + (* A handle to a given `glyph slot'. A slot is a container where it *) + (* is possible to load any one of the glyphs contained in its parent *) + (* face. *) (* *) - (* <Values> *) - (* FT_PIXEL_MODE_NONE :: *) - (* Value 0 is reserved. *) - (* *) - (* FT_PIXEL_MODE_MONO :: *) - (* A monochrome bitmap, using 1 bit per pixel. Note that pixels *) - (* are stored in most-significant order (MSB), which means that *) - (* the left-most pixel in a byte has value 128. *) - (* *) - (* FT_PIXEL_MODE_GRAY :: *) - (* An 8-bit bitmap, generally used to represent anti-aliased glyph *) - (* images. Each pixel is stored in one byte. Note that the number *) - (* of value `gray' levels is stored in the `num_bytes' field of *) - (* the @FT_Bitmap structure (it generally is 256). *) - (* *) - (* FT_PIXEL_MODE_GRAY2 :: *) - (* A 2-bit/pixel bitmap, used to represent embedded anti-aliased *) - (* bitmaps in font files according to the OpenType specification. *) - (* We haven't found a single font using this format, however. *) - (* *) - (* FT_PIXEL_MODE_GRAY4 :: *) - (* A 4-bit/pixel bitmap, used to represent embedded anti-aliased *) - (* bitmaps in font files according to the OpenType specification. *) - (* We haven't found a single font using this format, however. *) - (* *) - (* FT_PIXEL_MODE_LCD :: *) - (* An 8-bit bitmap, used to represent RGB or BGR decimated glyph *) - (* images used for display on LCD displays; the bitmap is three *) - (* times wider than the original glyph image. See also *) - (* @FT_RENDER_MODE_LCD. *) - (* *) - (* FT_PIXEL_MODE_LCD_V :: *) - (* An 8-bit bitmap, used to represent RGB or BGR decimated glyph *) - (* images used for display on rotated LCD displays; the bitmap *) - (* is three times taller than the original glyph image. See also *) - (* @FT_RENDER_MODE_LCD_V. *) + (* In other words, each time you call @FT_Load_Glyph or *) + (* @FT_Load_Char, the slot's content is erased by the new glyph data, *) + (* i.e. the glyph's metrics, its image (bitmap or outline), and *) + (* other control information. *) + (* *) + (* <Also> *) + (* @FT_GlyphSlotRec details the publicly accessible glyph fields. *) (* *) type - FT_Pixel_Mode = byte; -const - FT_PIXEL_MODE_NONE = 0; - FT_PIXEL_MODE_MONO = FT_PIXEL_MODE_NONE + 1; - FT_PIXEL_MODE_GRAY = FT_PIXEL_MODE_MONO + 1; - FT_PIXEL_MODE_GRAY2 = FT_PIXEL_MODE_GRAY + 1; - FT_PIXEL_MODE_GRAY4 = FT_PIXEL_MODE_GRAY2 + 1; - FT_PIXEL_MODE_LCD = FT_PIXEL_MODE_GRAY4 + 1; - FT_PIXEL_MODE_LCD_V = FT_PIXEL_MODE_LCD + 1; + FT_GlyphSlot = ^FT_GlyphSlotRec; - FT_PIXEL_MODE_MAX = FT_PIXEL_MODE_LCD_V + 1; (* do not remove *) + +{$DEFINE TYPE_DECL} +{$I ftconfig.inc} +{$I fttypes.inc} +{$I ftimage.inc} +{$I ftglyph.inc} +{$I ftstroke.inc} +{$I ftoutln.inc} +{$UNDEF TYPE_DECL} (*************************************************************************) @@ -674,7 +608,6 @@ const (* vertAdvance :: *) (* Advance height for vertical layout. *) (* *) -type FT_Glyph_Metrics = record width , height : FT_Pos; @@ -759,140 +692,6 @@ type (*************************************************************************) (* *) - (* <Struct> *) - (* FT_Vector *) - (* *) - (* <Description> *) - (* A simple structure used to store a 2D vector; coordinates are of *) - (* the FT_Pos type. *) - (* *) - (* <Fields> *) - (* x :: The horizontal coordinate. *) - (* y :: The vertical coordinate. *) - (* *) - PFT_Vector = ^FT_Vector; - FT_Vector = record - x , - y : FT_Pos; - end; - - - (*************************************************************************) - (* *) - (* <Struct> *) - (* FT_Outline *) - (* *) - (* <Description> *) - (* This structure is used to describe an outline to the scan-line *) - (* converter. *) - (* *) - (* <Fields> *) - (* n_contours :: The number of contours in the outline. *) - (* *) - (* n_points :: The number of points in the outline. *) - (* *) - (* points :: A pointer to an array of `n_points' FT_Vector *) - (* elements, giving the outline's point coordinates. *) - (* *) - (* tags :: A pointer to an array of `n_points' chars, giving *) - (* each outline point's type. If bit 0 is unset, the *) - (* point is `off' the curve, i.e. a Bezier control *) - (* point, while it is `on' when set. *) - (* *) - (* Bit 1 is meaningful for `off' points only. If set, *) - (* it indicates a third-order Bezier arc control point; *) - (* and a second-order control point if unset. *) - (* *) - (* contours :: An array of `n_contours' shorts, giving the end *) - (* point of each contour within the outline. For *) - (* example, the first contour is defined by the points *) - (* `0' to `contours[0]', the second one is defined by *) - (* the points `contours[0]+1' to `contours[1]', etc. *) - (* *) - (* flags :: A set of bit flags used to characterize the outline *) - (* and give hints to the scan-converter and hinter on *) - (* how to convert/grid-fit it. See FT_Outline_Flags. *) - (* *) - PFT_Outline = ^FT_Outline; - FT_Outline = record - n_contours : FT_Short; - n_points : FT_Short; - - points : PFT_Vector; - tags : PChar; - contours : PFT_Short; - - flags : FT_Int; - end; - - - (*************************************************************************) - (* *) - (* <Struct> *) - (* FT_Bitmap *) - (* *) - (* <Description> *) - (* A structure used to describe a bitmap or pixmap to the raster. *) - (* Note that we now manage pixmaps of various depths through the *) - (* `pixel_mode' field. *) - (* *) - (* <Fields> *) - (* rows :: The number of bitmap rows. *) - (* *) - (* width :: The number of pixels in bitmap row. *) - (* *) - (* pitch :: The pitch's absolute value is the number of bytes *) - (* taken by one bitmap row, including padding. *) - (* However, the pitch is positive when the bitmap has *) - (* a `down' flow, and negative when it has an `up' *) - (* flow. In all cases, the pitch is an offset to add *) - (* to a bitmap pointer in order to go down one row. *) - (* *) - (* buffer :: A typeless pointer to the bitmap buffer. This *) - (* value should be aligned on 32-bit boundaries in *) - (* most cases. *) - (* *) - (* num_grays :: This field is only used with *) - (* `FT_PIXEL_MODE_GRAY'; it gives the number of gray *) - (* levels used in the bitmap. *) - (* *) - (* pixel_mode :: The pixel mode, i.e., how pixel bits are stored. *) - (* See @FT_Pixel_Mode for possible values. *) - (* *) - (* palette_mode :: This field is only used with paletted pixel modes; *) - (* it indicates how the palette is stored. *) - (* *) - (* palette :: A typeless pointer to the bitmap palette; only *) - (* used for paletted pixel modes. *) - (* *) - (* <Note> *) - (* For now, the only pixel mode supported by FreeType are mono and *) - (* grays. However, drivers might be added in the future to support *) - (* more `colorful' options. *) - (* *) - (* When using pixel modes pal2, pal4 and pal8 with a void `palette' *) - (* field, a gray pixmap with respectively 4, 16, and 256 levels of *) - (* gray is assumed. This, in order to be compatible with some *) - (* embedded bitmap formats defined in the TrueType specification. *) - (* *) - (* Note that no font was found presenting such embedded bitmaps, so *) - (* this is currently completely unhandled by the library. *) - (* *) - PFT_Bitmap = ^FT_Bitmap; - FT_Bitmap = record - rows , - width : FT_Int; - pitch : FT_Int; - buffer : PByteArray; - num_grays : FT_Short; - pixel_mode , - palette_mode : byte; - palette : pointer; - end; - - - (*************************************************************************) - (* *) (* <Type> *) (* FT_Face *) (* *) @@ -950,184 +749,11 @@ type PAFT_CharMap = ^FT_CharMap; AFT_CharMap = array[0..High(Word)] of FT_CharMap; - (*************************************************************************) - (* *) - (* <Type> *) - (* FT_Library *) - (* *) - (* <Description> *) - (* A handle to a FreeType library instance. Each `library' is *) - (* completely independent from the others; it is the `root' of a set *) - (* of objects like fonts, faces, sizes, etc. *) - (* *) - (* It also embeds a memory manager (see @FT_Memory), as well as a *) - (* scan-line converter object (see @FT_Raster). *) - (* *) - (* <Note> *) - (* Library objects are normally created by @FT_Init_FreeType, and *) - (* destroyed with @FT_Done_FreeType. *) - (* *) - FT_Library = ^FT_LibraryRec; - FT_LibraryRec = record // internal - end; - (*************************************************************************) - (* *) - (* <Section> *) - (* glyph_management *) - (* *) - (* <Title> *) - (* Glyph Management *) - (* *) - (* <Abstract> *) - (* Generic interface to manage individual glyph data. *) - (* *) - (* <Description> *) - (* This section contains definitions used to manage glyph data *) - (* through generic FT_Glyph objects. Each of them can contain a *) - (* bitmap, a vector outline, or even images in other formats. *) - (* *) - (*************************************************************************) - (* forward declaration to a private type *) - PFT_Glyph_Class = ^FT_Glyph_Class; - FT_Glyph_Class = record // internal - end; - (*************************************************************************) - (* *) - (* <Type> *) - (* FT_Glyph *) - (* *) - (* <Description> *) - (* Handle to an object used to model generic glyph images. It is a *) - (* pointer to the @FT_GlyphRec structure and can contain a glyph *) - (* bitmap or pointer. *) - (* *) - (* <Note> *) - (* Glyph objects are not owned by the library. You must thus release *) - (* them manually (through @FT_Done_Glyph) _before_ calling *) - (* @FT_Done_FreeType. *) - (* *) - FT_Glyph = ^FT_GlyphRec; - - (*************************************************************************) - (* *) - (* <Struct> *) - (* FT_GlyphRec *) - (* *) - (* <Description> *) - (* The root glyph structure contains a given glyph image plus its *) - (* advance width in 16.16 fixed float format. *) - (* *) - (* <Fields> *) - (* library :: A handle to the FreeType library object. *) - (* *) - (* clazz :: A pointer to the glyph's class. Private. *) - (* *) - (* format :: The format of the glyph's image. *) - (* *) - (* advance :: A 16.16 vector that gives the glyph's advance width. *) - (* *) - FT_GlyphRec = record - library_: FT_Library; - clazz: PFT_Glyph_Class; - format: FT_Glyph_Format; - advance: FT_Vector; - end; - - - (*************************************************************************) - (* *) - (* <Type> *) - (* FT_BitmapGlyph *) - (* *) - (* <Description> *) - (* A handle to an object used to model a bitmap glyph image. This is *) - (* a sub-class of @FT_Glyph, and a pointer to @FT_BitmapGlyphRec. *) - (* *) - FT_BitmapGlyph = ^FT_BitmapGlyphRec; - - (*************************************************************************) - (* *) - (* <Struct> *) - (* FT_BitmapGlyphRec *) - (* *) - (* <Description> *) - (* A structure used for bitmap glyph images. This really is a *) - (* `sub-class' of `FT_GlyphRec'. *) - (* *) - (* <Fields> *) - (* root :: The root FT_Glyph fields. *) - (* *) - (* left :: The left-side bearing, i.e., the horizontal distance *) - (* from the current pen position to the left border of the *) - (* glyph bitmap. *) - (* *) - (* top :: The top-side bearing, i.e., the vertical distance from *) - (* the current pen position to the top border of the glyph *) - (* bitmap. This distance is positive for upwards-y! *) - (* *) - (* bitmap :: A descriptor for the bitmap. *) - (* *) - (* <Note> *) - (* You can typecast FT_Glyph to FT_BitmapGlyph if you have *) - (* glyph->format == FT_GLYPH_FORMAT_BITMAP. This lets you access *) - (* the bitmap's contents easily. *) - (* *) - (* The corresponding pixel buffer is always owned by the BitmapGlyph *) - (* and is thus created and destroyed with it. *) - (* *) - FT_BitmapGlyphRec = record - root: FT_GlyphRec; - left: FT_Int; - top: FT_Int; - bitmap: FT_Bitmap; - end; - (*************************************************************************) - (* *) - (* <Type> *) - (* FT_OutlineGlyph *) - (* *) - (* <Description> *) - (* A handle to an object used to model an outline glyph image. This *) - (* is a sub-class of @FT_Glyph, and a pointer to @FT_OutlineGlyphRec. *) - (* *) - FT_OutlineGlyph = ^FT_OutlineGlyphRec; - - (*************************************************************************) - (* *) - (* <Struct> *) - (* FT_OutlineGlyphRec *) - (* *) - (* <Description> *) - (* A structure used for outline (vectorial) glyph images. This *) - (* really is a `sub-class' of `FT_GlyphRec'. *) - (* *) - (* <Fields> *) - (* root :: The root FT_Glyph fields. *) - (* *) - (* outline :: A descriptor for the outline. *) - (* *) - (* <Note> *) - (* You can typecast FT_Glyph to FT_OutlineGlyph if you have *) - (* glyph->format == FT_GLYPH_FORMAT_OUTLINE. This lets you access *) - (* the outline's content easily. *) - (* *) - (* As the outline is extracted from a glyph slot, its coordinates are *) - (* expressed normally in 26.6 pixels, unless the flag *) - (* FT_LOAD_NO_SCALE was used in FT_Load_Glyph() or FT_Load_Char(). *) - (* *) - (* The outline's tables are always owned by the object and are *) - (* destroyed with it. *) - (* *) - FT_OutlineGlyphRec = record - root: FT_GlyphRec; - outline: FT_Outline; - end; - (*************************************************************************) (* *) @@ -1149,101 +775,6 @@ type (*************************************************************************) (* *) - (* <FuncType> *) - (* FT_Generic_Finalizer *) - (* *) - (* <Description> *) - (* Describes a function used to destroy the `client' data of any *) - (* FreeType object. See the description of the FT_Generic type for *) - (* details of usage. *) - (* *) - (* <Input> *) - (* The address of the FreeType object which is under finalization. *) - (* Its client data is accessed through its `generic' field. *) - (* *) - FT_Generic_Finalizer = procedure(AnObject : pointer ); cdecl; - - (*************************************************************************) - (* *) - (* <Struct> *) - (* FT_Generic *) - (* *) - (* <Description> *) - (* Client applications often need to associate their own data to a *) - (* variety of FreeType core objects. For example, a text layout API *) - (* might want to associate a glyph cache to a given size object. *) - (* *) - (* Most FreeType object contains a `generic' field, of type *) - (* FT_Generic, which usage is left to client applications and font *) - (* servers. *) - (* *) - (* It can be used to store a pointer to client-specific data, as well *) - (* as the address of a `finalizer' function, which will be called by *) - (* FreeType when the object is destroyed (for example, the previous *) - (* client example would put the address of the glyph cache destructor *) - (* in the `finalizer' field). *) - (* *) - (* <Fields> *) - (* data :: A typeless pointer to any client-specified data. This *) - (* field is completely ignored by the FreeType library. *) - (* *) - (* finalizer :: A pointer to a `generic finalizer' function, which *) - (* will be called when the object is destroyed. If this *) - (* field is set to NULL, no code will be called. *) - (* *) - FT_Generic = record - data : pointer; - finalizer : FT_Generic_Finalizer; - end; - - (*************************************************************************) - (* *) - (* <Struct> *) - (* FT_BBox *) - (* *) - (* <Description> *) - (* A structure used to hold an outline's bounding box, i.e., the *) - (* coordinates of its extrema in the horizontal and vertical *) - (* directions. *) - (* *) - (* <Fields> *) - (* xMin :: The horizontal minimum (left-most). *) - (* *) - (* yMin :: The vertical minimum (bottom-most). *) - (* *) - (* xMax :: The horizontal maximum (right-most). *) - (* *) - (* yMax :: The vertical maximum (top-most). *) - (* *) - PFT_BBox = ^FT_BBox; - FT_BBox = record - xMin, yMin : FT_Pos; - xMax, yMax : FT_Pos; - end; - - - (*************************************************************************) - (* *) - (* <Type> *) - (* FT_GlyphSlot *) - (* *) - (* <Description> *) - (* A handle to a given `glyph slot'. A slot is a container where it *) - (* is possible to load any one of the glyphs contained in its parent *) - (* face. *) - (* *) - (* In other words, each time you call @FT_Load_Glyph or *) - (* @FT_Load_Char, the slot's content is erased by the new glyph data, *) - (* i.e. the glyph's metrics, its image (bitmap or outline), and *) - (* other control information. *) - (* *) - (* <Also> *) - (* @FT_GlyphSlotRec details the publicly accessible glyph fields. *) - (* *) - FT_GlyphSlot = ^FT_GlyphSlotRec; - - (*************************************************************************) - (* *) (* <Struct> *) (* FT_GlyphSlotRec *) (* *) @@ -1432,7 +963,7 @@ type subglyphs : FT_SubGlyph; control_data : pointer; - control_len : longint; + control_len : clong; lsb_delta: FT_Pos; rsb_delta: FT_Pos; @@ -1497,15 +1028,15 @@ type (* computations. *) (* *) FT_Size_Metrics = record - x_ppem , - y_ppem : FT_UShort; - x_scale , - y_scale : FT_Fixed; - - ascender , - descender : FT_Pos; - height : FT_Pos; - max_advance : FT_Pos; + x_ppem, (* horizontal pixels per EM *) + y_ppem: FT_UShort; (* vertical pixels per EM *) + x_scale, (* scaling values used to convert font *) + y_scale: FT_Fixed; (* units to 26.6 fractional pixels *) + + ascender, (* ascender in 26.6 frac. pixels *) + descender: FT_Pos; (* descender in 26.6 frac. pixels *) + height: FT_Pos; (* text height in 26.6 frac. pixels *) + max_advance: FT_Pos; (* max horizontal advance, in 26.6 pixels *) end; (*************************************************************************) @@ -1786,21 +1317,29 @@ type encoding_id : FT_UShort; end; + +{$I ftconfig.inc} +{$I fttypes.inc} +{$I ftimage.inc} +{$I ftglyph.inc} +{$I ftstroke.inc} +{$I ftoutln.inc} + + { GLOBAL PROCEDURES } (*************************************************************************) (* *) (* @macro: *) - (* FT_CURVE_TAG ( flag ) *) + (* FT_HAS_KERNING( face ) *) (* *) - function FT_CURVE_TAG(flag: byte): byte; - -const - FT_CURVE_TAG_ON = 1; - FT_CURVE_TAG_CONIC = 0; - FT_CURVE_TAG_CUBIC = 2; - + (* @description: *) + (* A macro that returns true whenever a face object contains kerning *) + (* data that can be accessed with @FT_Get_Kerning. *) + (* *) + function FT_HAS_KERNING(face : FT_Face ) : cbool; + (*************************************************************************) (* *) (* @macro: *) @@ -1813,16 +1352,6 @@ const (* *) function FT_IS_SCALABLE(face : FT_Face ) : cbool; - (*************************************************************************) - (* *) - (* @macro: *) - (* FT_HAS_KERNING( face ) *) - (* *) - (* @description: *) - (* A macro that returns true whenever a face object contains kerning *) - (* data that can be accessed with @FT_Get_Kerning. *) - (* *) - function FT_HAS_KERNING(face : FT_Face ) : cbool; (*************************************************************************) (* *) @@ -2284,216 +1813,12 @@ const pixel_height : FT_UInt ) : FT_Error; cdecl; external ft_lib name 'FT_Set_Pixel_Sizes'; - (*************************************************************************) - (* *) - (* <Function> *) - (* FT_Get_Glyph *) - (* *) - (* <Description> *) - (* A function used to extract a glyph image from a slot. *) - (* *) - (* <Input> *) - (* slot :: A handle to the source glyph slot. *) - (* *) - (* <Output> *) - (* aglyph :: A handle to the glyph object. *) - (* *) - (* <Return> *) - (* FreeType error code. 0 means success. *) - (* *) - function FT_Get_Glyph( - slot: FT_GlyphSlot; - out aglyph: FT_Glyph ): FT_Error; - cdecl; external ft_lib name 'FT_Get_Glyph'; - - (*************************************************************************) - (* *) - (* <Enum> *) - (* FT_Glyph_BBox_Mode *) - (* *) - (* <Description> *) - (* The mode how the values of @FT_Glyph_Get_CBox are returned. *) - (* *) - (* <Values> *) - (* FT_GLYPH_BBOX_UNSCALED :: *) - (* Return unscaled font units. *) - (* *) - (* FT_GLYPH_BBOX_SUBPIXELS :: *) - (* Return unfitted 26.6 coordinates. *) - (* *) - (* FT_GLYPH_BBOX_GRIDFIT :: *) - (* Return grid-fitted 26.6 coordinates. *) - (* *) - (* FT_GLYPH_BBOX_TRUNCATE :: *) - (* Return coordinates in integer pixels. *) - (* *) - (* FT_GLYPH_BBOX_PIXELS :: *) - (* Return grid-fitted pixel coordinates. *) - (* *) -type - FT_Glyph_BBox_Mode = FT_UInt; const - FT_GLYPH_BBOX_UNSCALED = 0; - FT_GLYPH_BBOX_SUBPIXELS = 0; - FT_GLYPH_BBOX_GRIDFIT = 1; - FT_GLYPH_BBOX_TRUNCATE = 2; - FT_GLYPH_BBOX_PIXELS = 3; - - (*************************************************************************) - (* *) - (* <Function> *) - (* FT_Glyph_Get_CBox *) - (* *) - (* <Description> *) - (* Return a glyph's `control box'. The control box encloses all the *) - (* outline's points, including Bézier control points. Though it *) - (* coincides with the exact bounding box for most glyphs, it can be *) - (* slightly larger in some situations (like when rotating an outline *) - (* which contains Bézier outside arcs). *) - (* *) - (* Computing the control box is very fast, while getting the bounding *) - (* box can take much more time as it needs to walk over all segments *) - (* and arcs in the outline. To get the latter, you can use the *) - (* `ftbbox' component which is dedicated to this single task. *) - (* *) - (* <Input> *) - (* glyph :: A handle to the source glyph object. *) - (* *) - (* mode :: The mode which indicates how to interpret the returned *) - (* bounding box values. *) - (* *) - (* <Output> *) - (* acbox :: The glyph coordinate bounding box. Coordinates are *) - (* expressed in 1/64th of pixels if it is grid-fitted. *) - (* *) - (* <Note> *) - (* Coordinates are relative to the glyph origin, using the Y-upwards *) - (* convention. *) - (* *) - (* If the glyph has been loaded with @FT_LOAD_NO_SCALE, `bbox_mode' *) - (* must be set to @FT_GLYPH_BBOX_UNSCALED to get unscaled font *) - (* units in 26.6 pixel format. The value @FT_GLYPH_BBOX_SUBPIXELS *) - (* is another name for this constant. *) - (* *) - (* Note that the maximum coordinates are exclusive, which means that *) - (* one can compute the width and height of the glyph image (be it in *) - (* integer or 26.6 pixels) as: *) - (* *) - (* { *) - (* width = bbox.xMax - bbox.xMin; *) - (* height = bbox.yMax - bbox.yMin; *) - (* } *) - (* *) - (* Note also that for 26.6 coordinates, if `bbox_mode' is set to *) - (* @FT_GLYPH_BBOX_GRIDFIT, the coordinates will also be grid-fitted, *) - (* which corresponds to: *) - (* *) - (* { *) - (* bbox.xMin = FLOOR(bbox.xMin); *) - (* bbox.yMin = FLOOR(bbox.yMin); *) - (* bbox.xMax = CEILING(bbox.xMax); *) - (* bbox.yMax = CEILING(bbox.yMax); *) - (* } *) - (* *) - (* To get the bbox in pixel coordinates, set `bbox_mode' to *) - (* @FT_GLYPH_BBOX_TRUNCATE. *) - (* *) - (* To get the bbox in grid-fitted pixel coordinates, set `bbox_mode' *) - (* to @FT_GLYPH_BBOX_PIXELS. *) - (* *) - procedure FT_Glyph_Get_CBox( glyph: FT_Glyph; - bbox_mode: FT_UInt; - out acbox: FT_BBox ); - cdecl; external ft_lib name 'FT_Glyph_Get_CBox'; - - (*************************************************************************) - (* *) - (* <Function> *) - (* FT_Glyph_To_Bitmap *) - (* *) - (* <Description> *) - (* Converts a given glyph object to a bitmap glyph object. *) - (* *) - (* <InOut> *) - (* the_glyph :: A pointer to a handle to the target glyph. *) - (* *) - (* <Input> *) - (* render_mode :: An enumeration that describe how the data is *) - (* rendered. *) - (* *) - (* origin :: A pointer to a vector used to translate the glyph *) - (* image before rendering. Can be 0 (if no *) - (* translation). The origin is expressed in *) - (* 26.6 pixels. *) - (* *) - (* destroy :: A boolean that indicates that the original glyph *) - (* image should be destroyed by this function. It is *) - (* never destroyed in case of error. *) - (* *) - (* <Return> *) - (* FreeType error code. 0 means success. *) - (* *) - (* <Note> *) - (* The glyph image is translated with the `origin' vector before *) - (* rendering. *) - (* *) - (* The first parameter is a pointer to a FT_Glyph handle, that will *) - (* be replaced by this function. Typically, you would use (omitting *) - (* error handling): *) - (* *) - (* *) - (* { *) - (* FT_Glyph glyph; *) - (* FT_BitmapGlyph glyph_bitmap; *) - (* *) - (* *) - (* // load glyph *) - (* error = FT_Load_Char( face, glyph_index, FT_LOAD_DEFAUT ); *) - (* *) - (* // extract glyph image *) - (* error = FT_Get_Glyph( face->glyph, &glyph ); *) - (* *) - (* // convert to a bitmap (default render mode + destroy old) *) - (* if ( glyph->format != FT_GLYPH_FORMAT_BITMAP ) *) - (* { *) - (* error = FT_Glyph_To_Bitmap( &glyph, FT_RENDER_MODE_DEFAULT, *) - (* 0, 1 ); *) - (* if ( error ) // glyph unchanged *) - (* ... *) - (* } *) - (* *) - (* // access bitmap content by typecasting *) - (* glyph_bitmap = (FT_BitmapGlyph)glyph; *) - (* *) - (* // do funny stuff with it, like blitting/drawing *) - (* ... *) - (* *) - (* // discard glyph image (bitmap or not) *) - (* FT_Done_Glyph( glyph ); *) - (* } *) - (* *) - (* *) - (* This function does nothing if the glyph format isn't scalable. *) - (* *) - function FT_Glyph_To_Bitmap(var the_glyph: FT_Glyph; - render_mode: FT_Render_Mode; - origin: PFT_Vector; - destroy: FT_Bool ): FT_Error; - cdecl; external ft_lib name 'FT_Glyph_To_Bitmap'; + FT_ANGLE_PI = 180 shl 16; + FT_ANGLE_2PI = FT_ANGLE_PI * 2; + FT_ANGLE_PI2 = FT_ANGLE_PI div 2; + FT_ANGLE_PI4 = FT_ANGLE_PI div 4; - (*************************************************************************) - (* *) - (* <Function> *) - (* FT_Done_Glyph *) - (* *) - (* <Description> *) - (* Destroys a given glyph. *) - (* *) - (* <Input> *) - (* glyph :: A handle to the target glyph object. *) - (* *) - procedure FT_Done_Glyph( glyph: FT_Glyph ); - cdecl; external ft_lib name 'FT_Done_Glyph'; implementation @@ -2504,17 +1829,17 @@ begin result := flag and 3; end; -{ FT_IS_SCALABLE } -function FT_IS_SCALABLE(face : FT_Face ) : cbool; -begin - result := cbool(face.face_flags and FT_FACE_FLAG_SCALABLE ); -end; - { FT_HAS_KERNING } function FT_HAS_KERNING(face : FT_Face ) : cbool; begin result := cbool(face.face_flags and FT_FACE_FLAG_KERNING ); end; +{ FT_IS_SCALABLE } +function FT_IS_SCALABLE(face : FT_Face ) : cbool; +begin + result := cbool(face.face_flags and FT_FACE_FLAG_SCALABLE ); +end; + end. diff --git a/Lua/src/lib/freetype/ftconfig.inc b/Lua/src/lib/freetype/ftconfig.inc new file mode 100644 index 00000000..100fb2e0 --- /dev/null +++ b/Lua/src/lib/freetype/ftconfig.inc @@ -0,0 +1,35 @@ +(***************************************************************************) +(* *) +(* ftconfig.h *) +(* *) +(* ANSI-specific configuration file (specification only). *) +(* *) +(* Copyright 1996-2001, 2002, 2003, 2004, 2006, 2007 by *) +(* David Turner, Robert Wilhelm, and Werner Lemberg. *) +(* *) +(* This file is part of the FreeType project, and may only be used, *) +(* modified, and distributed under the terms of the FreeType project *) +(* license, LICENSE.TXT. By continuing to use, modify, or distribute *) +(* this file you indicate that you have read the license and *) +(* understand and accept it fully. *) +(* *) +(***************************************************************************) +(***************************************************************************) +(* Pascal port by the UltraStar Deluxe Team *) +(***************************************************************************) + +{$IFDEF TYPE_DECL} + + (*************************************************************************) + (* *) + (* IntN types *) + (* *) + (* Used to guarantee the size of some specific integers. *) + (* *) + FT_Int16 = cint16; + FT_UInt16 = cuint16; + FT_Int32 = cint32; + FT_UInt32 = cuint32; + +{$ENDIF TYPE_DECL} + diff --git a/Lua/src/lib/freetype/ftglyph.inc b/Lua/src/lib/freetype/ftglyph.inc new file mode 100644 index 00000000..0d4acc99 --- /dev/null +++ b/Lua/src/lib/freetype/ftglyph.inc @@ -0,0 +1,435 @@ +(***************************************************************************) +(* *) +(* ftglyph.h *) +(* *) +(* FreeType convenience functions to handle glyphs (specification). *) +(* *) +(* Copyright 1996-2001, 2002, 2003, 2006 by *) +(* David Turner, Robert Wilhelm, and Werner Lemberg. *) +(* *) +(* This file is part of the FreeType project, and may only be used, *) +(* modified, and distributed under the terms of the FreeType project *) +(* license, LICENSE.TXT. By continuing to use, modify, or distribute *) +(* this file you indicate that you have read the license and *) +(* understand and accept it fully. *) +(* *) +(***************************************************************************) +(***************************************************************************) +(* Pascal port by the UltraStar Deluxe Team *) +(***************************************************************************) + + + (*************************************************************************) + (* *) + (* This file contains the definition of several convenience functions *) + (* that can be used by client applications to easily retrieve glyph *) + (* bitmaps and outlines from a given face. *) + (* *) + (* These functions should be optional if you are writing a font server *) + (* or text layout engine on top of FreeType. However, they are pretty *) + (* handy for many other simple uses of the library. *) + (* *) + (*************************************************************************) + + (*************************************************************************) + (* *) + (* <Section> *) + (* glyph_management *) + (* *) + (* <Title> *) + (* Glyph Management *) + (* *) + (* <Abstract> *) + (* Generic interface to manage individual glyph data. *) + (* *) + (* <Description> *) + (* This section contains definitions used to manage glyph data *) + (* through generic FT_Glyph objects. Each of them can contain a *) + (* bitmap, a vector outline, or even images in other formats. *) + (* *) + (*************************************************************************) + +{$IFDEF TYPE_DECL} + + (* forward declaration to a private type *) + PFT_Glyph_Class = Pointer; + + + (*************************************************************************) + (* *) + (* <Type> *) + (* FT_Glyph *) + (* *) + (* <Description> *) + (* Handle to an object used to model generic glyph images. It is a *) + (* pointer to the @FT_GlyphRec structure and can contain a glyph *) + (* bitmap or pointer. *) + (* *) + (* <Note> *) + (* Glyph objects are not owned by the library. You must thus release *) + (* them manually (through @FT_Done_Glyph) _before_ calling *) + (* @FT_Done_FreeType. *) + (* *) + FT_Glyph = ^FT_GlyphRec; + + (*************************************************************************) + (* *) + (* <Struct> *) + (* FT_GlyphRec *) + (* *) + (* <Description> *) + (* The root glyph structure contains a given glyph image plus its *) + (* advance width in 16.16 fixed float format. *) + (* *) + (* <Fields> *) + (* library :: A handle to the FreeType library object. *) + (* *) + (* clazz :: A pointer to the glyph's class. Private. *) + (* *) + (* format :: The format of the glyph's image. *) + (* *) + (* advance :: A 16.16 vector that gives the glyph's advance width. *) + (* *) + FT_GlyphRec = record + library_: FT_Library; + clazz: PFT_Glyph_Class; + format: FT_Glyph_Format; + advance: FT_Vector; + end; + + + (*************************************************************************) + (* *) + (* <Type> *) + (* FT_BitmapGlyph *) + (* *) + (* <Description> *) + (* A handle to an object used to model a bitmap glyph image. This is *) + (* a sub-class of @FT_Glyph, and a pointer to @FT_BitmapGlyphRec. *) + (* *) + FT_BitmapGlyph = ^FT_BitmapGlyphRec; + + (*************************************************************************) + (* *) + (* <Struct> *) + (* FT_BitmapGlyphRec *) + (* *) + (* <Description> *) + (* A structure used for bitmap glyph images. This really is a *) + (* `sub-class' of `FT_GlyphRec'. *) + (* *) + (* <Fields> *) + (* root :: The root FT_Glyph fields. *) + (* *) + (* left :: The left-side bearing, i.e., the horizontal distance *) + (* from the current pen position to the left border of the *) + (* glyph bitmap. *) + (* *) + (* top :: The top-side bearing, i.e., the vertical distance from *) + (* the current pen position to the top border of the glyph *) + (* bitmap. This distance is positive for upwards-y! *) + (* *) + (* bitmap :: A descriptor for the bitmap. *) + (* *) + (* <Note> *) + (* You can typecast FT_Glyph to FT_BitmapGlyph if you have *) + (* glyph->format == FT_GLYPH_FORMAT_BITMAP. This lets you access *) + (* the bitmap's contents easily. *) + (* *) + (* The corresponding pixel buffer is always owned by the BitmapGlyph *) + (* and is thus created and destroyed with it. *) + (* *) + FT_BitmapGlyphRec = record + root: FT_GlyphRec; + left: FT_Int; + top: FT_Int; + bitmap: FT_Bitmap; + end; + + + (*************************************************************************) + (* *) + (* <Type> *) + (* FT_OutlineGlyph *) + (* *) + (* <Description> *) + (* A handle to an object used to model an outline glyph image. This *) + (* is a sub-class of @FT_Glyph, and a pointer to @FT_OutlineGlyphRec. *) + (* *) + FT_OutlineGlyph = ^FT_OutlineGlyphRec; + + + (*************************************************************************) + (* *) + (* <Struct> *) + (* FT_OutlineGlyphRec *) + (* *) + (* <Description> *) + (* A structure used for outline (vectorial) glyph images. This *) + (* really is a `sub-class' of `FT_GlyphRec'. *) + (* *) + (* <Fields> *) + (* root :: The root FT_Glyph fields. *) + (* *) + (* outline :: A descriptor for the outline. *) + (* *) + (* <Note> *) + (* You can typecast FT_Glyph to FT_OutlineGlyph if you have *) + (* glyph->format == FT_GLYPH_FORMAT_OUTLINE. This lets you access *) + (* the outline's content easily. *) + (* *) + (* As the outline is extracted from a glyph slot, its coordinates are *) + (* expressed normally in 26.6 pixels, unless the flag *) + (* FT_LOAD_NO_SCALE was used in FT_Load_Glyph() or FT_Load_Char(). *) + (* *) + (* The outline's tables are always owned by the object and are *) + (* destroyed with it. *) + (* *) + FT_OutlineGlyphRec = record + root: FT_GlyphRec; + outline: FT_Outline; + end; + +{$ELSE TYPE_DECL} + + (*************************************************************************) + (* *) + (* <Function> *) + (* FT_Get_Glyph *) + (* *) + (* <Description> *) + (* A function used to extract a glyph image from a slot. *) + (* *) + (* <Input> *) + (* slot :: A handle to the source glyph slot. *) + (* *) + (* <Output> *) + (* aglyph :: A handle to the glyph object. *) + (* *) + (* <Return> *) + (* FreeType error code. 0 means success. *) + (* *) + function FT_Get_Glyph( + slot: FT_GlyphSlot; + out aglyph: FT_Glyph ): FT_Error; + cdecl; external ft_lib name 'FT_Get_Glyph'; + + (*************************************************************************) + (* *) + (* <Function> *) + (* FT_Glyph_Copy *) + (* *) + (* <Description> *) + (* A function used to copy a glyph image. Note that the created *) + (* @FT_Glyph object must be released with @FT_Done_Glyph. *) + (* *) + (* <Input> *) + (* source :: A handle to the source glyph object. *) + (* *) + (* <Output> *) + (* target :: A handle to the target glyph object. 0~in case of *) + (* error. *) + (* *) + (* <Return> *) + (* FreeType error code. 0~means success. *) + (* *) + function FT_Glyph_Copy(source: FT_Glyph; + var target: FT_Glyph ): FT_Error; + cdecl; external ft_lib name 'FT_Glyph_Copy'; + +{$ENDIF TYPE_DECL} +{$IFDEF TYPE_DECL} + + (*************************************************************************) + (* *) + (* <Enum> *) + (* FT_Glyph_BBox_Mode *) + (* *) + (* <Description> *) + (* The mode how the values of @FT_Glyph_Get_CBox are returned. *) + (* *) + (* <Values> *) + (* FT_GLYPH_BBOX_UNSCALED :: *) + (* Return unscaled font units. *) + (* *) + (* FT_GLYPH_BBOX_SUBPIXELS :: *) + (* Return unfitted 26.6 coordinates. *) + (* *) + (* FT_GLYPH_BBOX_GRIDFIT :: *) + (* Return grid-fitted 26.6 coordinates. *) + (* *) + (* FT_GLYPH_BBOX_TRUNCATE :: *) + (* Return coordinates in integer pixels. *) + (* *) + (* FT_GLYPH_BBOX_PIXELS :: *) + (* Return grid-fitted pixel coordinates. *) + (* *) + FT_Glyph_BBox_Mode = cint; +{$ELSE TYPE_DECL} +const + FT_GLYPH_BBOX_UNSCALED = 0; + FT_GLYPH_BBOX_SUBPIXELS = 0; + FT_GLYPH_BBOX_GRIDFIT = 1; + FT_GLYPH_BBOX_TRUNCATE = 2; + FT_GLYPH_BBOX_PIXELS = 3; + + + (*************************************************************************) + (* *) + (* <Function> *) + (* FT_Glyph_Get_CBox *) + (* *) + (* <Description> *) + (* Return a glyph's `control box'. The control box encloses all the *) + (* outline's points, including Bézier control points. Though it *) + (* coincides with the exact bounding box for most glyphs, it can be *) + (* slightly larger in some situations (like when rotating an outline *) + (* which contains Bézier outside arcs). *) + (* *) + (* Computing the control box is very fast, while getting the bounding *) + (* box can take much more time as it needs to walk over all segments *) + (* and arcs in the outline. To get the latter, you can use the *) + (* `ftbbox' component which is dedicated to this single task. *) + (* *) + (* <Input> *) + (* glyph :: A handle to the source glyph object. *) + (* *) + (* mode :: The mode which indicates how to interpret the returned *) + (* bounding box values. *) + (* *) + (* <Output> *) + (* acbox :: The glyph coordinate bounding box. Coordinates are *) + (* expressed in 1/64th of pixels if it is grid-fitted. *) + (* *) + (* <Note> *) + (* Coordinates are relative to the glyph origin, using the Y-upwards *) + (* convention. *) + (* *) + (* If the glyph has been loaded with @FT_LOAD_NO_SCALE, `bbox_mode' *) + (* must be set to @FT_GLYPH_BBOX_UNSCALED to get unscaled font *) + (* units in 26.6 pixel format. The value @FT_GLYPH_BBOX_SUBPIXELS *) + (* is another name for this constant. *) + (* *) + (* Note that the maximum coordinates are exclusive, which means that *) + (* one can compute the width and height of the glyph image (be it in *) + (* integer or 26.6 pixels) as: *) + (* *) + (* { *) + (* width = bbox.xMax - bbox.xMin; *) + (* height = bbox.yMax - bbox.yMin; *) + (* } *) + (* *) + (* Note also that for 26.6 coordinates, if `bbox_mode' is set to *) + (* @FT_GLYPH_BBOX_GRIDFIT, the coordinates will also be grid-fitted, *) + (* which corresponds to: *) + (* *) + (* { *) + (* bbox.xMin = FLOOR(bbox.xMin); *) + (* bbox.yMin = FLOOR(bbox.yMin); *) + (* bbox.xMax = CEILING(bbox.xMax); *) + (* bbox.yMax = CEILING(bbox.yMax); *) + (* } *) + (* *) + (* To get the bbox in pixel coordinates, set `bbox_mode' to *) + (* @FT_GLYPH_BBOX_TRUNCATE. *) + (* *) + (* To get the bbox in grid-fitted pixel coordinates, set `bbox_mode' *) + (* to @FT_GLYPH_BBOX_PIXELS. *) + (* *) + procedure FT_Glyph_Get_CBox( glyph: FT_Glyph; + bbox_mode: FT_UInt; + out acbox: FT_BBox ); + cdecl; external ft_lib name 'FT_Glyph_Get_CBox'; + + + (*************************************************************************) + (* *) + (* <Function> *) + (* FT_Glyph_To_Bitmap *) + (* *) + (* <Description> *) + (* Converts a given glyph object to a bitmap glyph object. *) + (* *) + (* <InOut> *) + (* the_glyph :: A pointer to a handle to the target glyph. *) + (* *) + (* <Input> *) + (* render_mode :: An enumeration that describe how the data is *) + (* rendered. *) + (* *) + (* origin :: A pointer to a vector used to translate the glyph *) + (* image before rendering. Can be 0 (if no *) + (* translation). The origin is expressed in *) + (* 26.6 pixels. *) + (* *) + (* destroy :: A boolean that indicates that the original glyph *) + (* image should be destroyed by this function. It is *) + (* never destroyed in case of error. *) + (* *) + (* <Return> *) + (* FreeType error code. 0 means success. *) + (* *) + (* <Note> *) + (* The glyph image is translated with the `origin' vector before *) + (* rendering. *) + (* *) + (* The first parameter is a pointer to a FT_Glyph handle, that will *) + (* be replaced by this function. Typically, you would use (omitting *) + (* error handling): *) + (* *) + (* *) + (* { *) + (* FT_Glyph glyph; *) + (* FT_BitmapGlyph glyph_bitmap; *) + (* *) + (* *) + (* // load glyph *) + (* error = FT_Load_Char( face, glyph_index, FT_LOAD_DEFAUT ); *) + (* *) + (* // extract glyph image *) + (* error = FT_Get_Glyph( face->glyph, &glyph ); *) + (* *) + (* // convert to a bitmap (default render mode + destroy old) *) + (* if ( glyph->format != FT_GLYPH_FORMAT_BITMAP ) *) + (* { *) + (* error = FT_Glyph_To_Bitmap( &glyph, FT_RENDER_MODE_DEFAULT, *) + (* 0, 1 ); *) + (* if ( error ) // glyph unchanged *) + (* ... *) + (* } *) + (* *) + (* // access bitmap content by typecasting *) + (* glyph_bitmap = (FT_BitmapGlyph)glyph; *) + (* *) + (* // do funny stuff with it, like blitting/drawing *) + (* ... *) + (* *) + (* // discard glyph image (bitmap or not) *) + (* FT_Done_Glyph( glyph ); *) + (* } *) + (* *) + (* *) + (* This function does nothing if the glyph format isn't scalable. *) + (* *) + function FT_Glyph_To_Bitmap(var the_glyph: FT_Glyph; + render_mode: FT_Render_Mode; + origin: PFT_Vector; + destroy: FT_Bool ): FT_Error; + cdecl; external ft_lib name 'FT_Glyph_To_Bitmap'; + + + (*************************************************************************) + (* *) + (* <Function> *) + (* FT_Done_Glyph *) + (* *) + (* <Description> *) + (* Destroys a given glyph. *) + (* *) + (* <Input> *) + (* glyph :: A handle to the target glyph object. *) + (* *) + procedure FT_Done_Glyph( glyph: FT_Glyph ); + cdecl; external ft_lib name 'FT_Done_Glyph'; + +{$ENDIF TYPE_DECL} diff --git a/Lua/src/lib/freetype/ftimage.inc b/Lua/src/lib/freetype/ftimage.inc new file mode 100644 index 00000000..9255c422 --- /dev/null +++ b/Lua/src/lib/freetype/ftimage.inc @@ -0,0 +1,803 @@ +(***************************************************************************) +(* *) +(* ftimage.h *) +(* *) +(* FreeType glyph image formats and default raster interface *) +(* (specification). *) +(* *) +(* Copyright 1996-2001, 2002, 2003, 2004, 2005, 2006, 2007 by *) +(* David Turner, Robert Wilhelm, and Werner Lemberg. *) +(* *) +(* This file is part of the FreeType project, and may only be used, *) +(* modified, and distributed under the terms of the FreeType project *) +(* license, LICENSE.TXT. By continuing to use, modify, or distribute *) +(* this file you indicate that you have read the license and *) +(* understand and accept it fully. *) +(* *) +(***************************************************************************) +(***************************************************************************) +(* Pascal port by the UltraStar Deluxe Team *) +(***************************************************************************) + + (*************************************************************************) + (* *) + (* Note: A `raster' is simply a scan-line converter, used to render *) + (* FT_Outlines into FT_Bitmaps. *) + (* *) + (*************************************************************************) + +{$IFDEF TYPE_DECL} + + (*************************************************************************) + (* *) + (* <Type> *) + (* FT_Pos *) + (* *) + (* <Description> *) + (* The type FT_Pos is a 32-bit integer used to store vectorial *) + (* coordinates. Depending on the context, these can represent *) + (* distances in integer font units, or 16,16, or 26.6 fixed float *) + (* pixel coordinates. *) + (* *) + FT_Pos = cslong; + + + (*************************************************************************) + (* *) + (* <Struct> *) + (* FT_Vector *) + (* *) + (* <Description> *) + (* A simple structure used to store a 2D vector; coordinates are of *) + (* the FT_Pos type. *) + (* *) + (* <Fields> *) + (* x :: The horizontal coordinate. *) + (* y :: The vertical coordinate. *) + (* *) + PFT_Vector = ^FT_Vector; + FT_Vector = record + x , + y : FT_Pos; + end; + + PFT_VectorArray = ^FT_VectorArray; + FT_VectorArray = array[0 .. (MaxInt div SizeOf(FT_Vector))-1] of FT_Vector; + + (*************************************************************************) + (* *) + (* <Struct> *) + (* FT_BBox *) + (* *) + (* <Description> *) + (* A structure used to hold an outline's bounding box, i.e., the *) + (* coordinates of its extrema in the horizontal and vertical *) + (* directions. *) + (* *) + (* <Fields> *) + (* xMin :: The horizontal minimum (left-most). *) + (* *) + (* yMin :: The vertical minimum (bottom-most). *) + (* *) + (* xMax :: The horizontal maximum (right-most). *) + (* *) + (* yMax :: The vertical maximum (top-most). *) + (* *) + PFT_BBox = ^FT_BBox; + FT_BBox = record + xMin, yMin : FT_Pos; + xMax, yMax : FT_Pos; + end; + + + (*************************************************************************) + (* *) + (* <Enum> *) + (* FT_Pixel_Mode *) + (* *) + (* <Description> *) + (* An enumeration type used to describe the format of pixels in a *) + (* given bitmap. Note that additional formats may be added in the *) + (* future. *) + (* *) + (* <Values> *) + (* FT_PIXEL_MODE_NONE :: *) + (* Value 0 is reserved. *) + (* *) + (* FT_PIXEL_MODE_MONO :: *) + (* A monochrome bitmap, using 1 bit per pixel. Note that pixels *) + (* are stored in most-significant order (MSB), which means that *) + (* the left-most pixel in a byte has value 128. *) + (* *) + (* FT_PIXEL_MODE_GRAY :: *) + (* An 8-bit bitmap, generally used to represent anti-aliased glyph *) + (* images. Each pixel is stored in one byte. Note that the number *) + (* of value `gray' levels is stored in the `num_bytes' field of *) + (* the @FT_Bitmap structure (it generally is 256). *) + (* *) + (* FT_PIXEL_MODE_GRAY2 :: *) + (* A 2-bit/pixel bitmap, used to represent embedded anti-aliased *) + (* bitmaps in font files according to the OpenType specification. *) + (* We haven't found a single font using this format, however. *) + (* *) + (* FT_PIXEL_MODE_GRAY4 :: *) + (* A 4-bit/pixel bitmap, used to represent embedded anti-aliased *) + (* bitmaps in font files according to the OpenType specification. *) + (* We haven't found a single font using this format, however. *) + (* *) + (* FT_PIXEL_MODE_LCD :: *) + (* An 8-bit bitmap, used to represent RGB or BGR decimated glyph *) + (* images used for display on LCD displays; the bitmap is three *) + (* times wider than the original glyph image. See also *) + (* @FT_RENDER_MODE_LCD. *) + (* *) + (* FT_PIXEL_MODE_LCD_V :: *) + (* An 8-bit bitmap, used to represent RGB or BGR decimated glyph *) + (* images used for display on rotated LCD displays; the bitmap *) + (* is three times taller than the original glyph image. See also *) + (* @FT_RENDER_MODE_LCD_V. *) + (* *) + FT_Pixel_Mode = cint; +{$ELSE TYPE_DECL} +const + FT_PIXEL_MODE_NONE = 0; + FT_PIXEL_MODE_MONO = FT_PIXEL_MODE_NONE + 1; + FT_PIXEL_MODE_GRAY = FT_PIXEL_MODE_MONO + 1; + FT_PIXEL_MODE_GRAY2 = FT_PIXEL_MODE_GRAY + 1; + FT_PIXEL_MODE_GRAY4 = FT_PIXEL_MODE_GRAY2 + 1; + FT_PIXEL_MODE_LCD = FT_PIXEL_MODE_GRAY4 + 1; + FT_PIXEL_MODE_LCD_V = FT_PIXEL_MODE_LCD + 1; + + FT_PIXEL_MODE_MAX = FT_PIXEL_MODE_LCD_V + 1; (* do not remove *) +{$ENDIF TYPE_DECL} +{$IFDEF TYPE_DECL} + + (*************************************************************************) + (* *) + (* <Struct> *) + (* FT_Bitmap *) + (* *) + (* <Description> *) + (* A structure used to describe a bitmap or pixmap to the raster. *) + (* Note that we now manage pixmaps of various depths through the *) + (* `pixel_mode' field. *) + (* *) + (* <Fields> *) + (* rows :: The number of bitmap rows. *) + (* *) + (* width :: The number of pixels in bitmap row. *) + (* *) + (* pitch :: The pitch's absolute value is the number of bytes *) + (* taken by one bitmap row, including padding. *) + (* However, the pitch is positive when the bitmap has *) + (* a `down' flow, and negative when it has an `up' *) + (* flow. In all cases, the pitch is an offset to add *) + (* to a bitmap pointer in order to go down one row. *) + (* *) + (* buffer :: A typeless pointer to the bitmap buffer. This *) + (* value should be aligned on 32-bit boundaries in *) + (* most cases. *) + (* *) + (* num_grays :: This field is only used with *) + (* `FT_PIXEL_MODE_GRAY'; it gives the number of gray *) + (* levels used in the bitmap. *) + (* *) + (* pixel_mode :: The pixel mode, i.e., how pixel bits are stored. *) + (* See @FT_Pixel_Mode for possible values. *) + (* *) + (* palette_mode :: This field is only used with paletted pixel modes; *) + (* it indicates how the palette is stored. *) + (* *) + (* palette :: A typeless pointer to the bitmap palette; only *) + (* used for paletted pixel modes. *) + (* *) + (* <Note> *) + (* For now, the only pixel mode supported by FreeType are mono and *) + (* grays. However, drivers might be added in the future to support *) + (* more `colorful' options. *) + (* *) + (* When using pixel modes pal2, pal4 and pal8 with a void `palette' *) + (* field, a gray pixmap with respectively 4, 16, and 256 levels of *) + (* gray is assumed. This, in order to be compatible with some *) + (* embedded bitmap formats defined in the TrueType specification. *) + (* *) + (* Note that no font was found presenting such embedded bitmaps, so *) + (* this is currently completely unhandled by the library. *) + (* *) + PFT_Bitmap = ^FT_Bitmap; + FT_Bitmap = record + rows: FT_Int; + width: FT_Int; + pitch: FT_Int; + buffer: PByteArray; + num_grays: FT_Short; + pixel_mode: byte; + palette_mode: byte; + palette: pointer; + end; + + + (*************************************************************************) + (* *) + (* <Section> *) + (* outline_processing *) + (* *) + (*************************************************************************) + + + (*************************************************************************) + (* *) + (* <Struct> *) + (* FT_Outline *) + (* *) + (* <Description> *) + (* This structure is used to describe an outline to the scan-line *) + (* converter. *) + (* *) + (* <Fields> *) + (* n_contours :: The number of contours in the outline. *) + (* *) + (* n_points :: The number of points in the outline. *) + (* *) + (* points :: A pointer to an array of `n_points' FT_Vector *) + (* elements, giving the outline's point coordinates. *) + (* *) + (* tags :: A pointer to an array of `n_points' chars, giving *) + (* each outline point's type. If bit 0 is unset, the *) + (* point is `off' the curve, i.e. a Bezier control *) + (* point, while it is `on' when set. *) + (* *) + (* Bit 1 is meaningful for `off' points only. If set, *) + (* it indicates a third-order Bezier arc control point; *) + (* and a second-order control point if unset. *) + (* *) + (* contours :: An array of `n_contours' shorts, giving the end *) + (* point of each contour within the outline. For *) + (* example, the first contour is defined by the points *) + (* `0' to `contours[0]', the second one is defined by *) + (* the points `contours[0]+1' to `contours[1]', etc. *) + (* *) + (* flags :: A set of bit flags used to characterize the outline *) + (* and give hints to the scan-converter and hinter on *) + (* how to convert/grid-fit it. See FT_Outline_Flags. *) + (* *) + PFT_Outline = ^FT_Outline; + FT_Outline = record + n_contours: FT_Short; (* number of contours in glyph *) + n_points: FT_Short; (* number of points in the glyph *) + + points: PFT_VectorArray; (* the outline's points *) + tags: PByteArray; (* the points flags *) + contours: PFT_ShortArray; (* the contour end points *) + + flags: FT_Int; (* outline masks *) + end; + +{$ELSE TYPE_DECL} + + (*************************************************************************) + (* *) + (* @macro: *) + (* FT_CURVE_TAG ( flag ) *) + (* *) + function FT_CURVE_TAG(flag: byte): byte; + +const + FT_CURVE_TAG_ON = 1; + FT_CURVE_TAG_CONIC = 0; + FT_CURVE_TAG_CUBIC = 2; + + FT_CURVE_TAG_TOUCH_X = 8; // reserved for the TrueType hinter + FT_CURVE_TAG_TOUCH_Y = 16; // reserved for the TrueType hinter + + FT_CURVE_TAG_TOUCH_BOTH = ( FT_CURVE_TAG_TOUCH_X or + FT_CURVE_TAG_TOUCH_Y ); +{$ENDIF TYPE_DECL} +{$IFDEF TYPE_DECL} + + (*************************************************************************) + (* *) + (* <FuncType> *) + (* FT_Outline_MoveToFunc *) + (* *) + (* <Description> *) + (* A function pointer type used to describe the signature of a `move *) + (* to' function during outline walking/decomposition. *) + (* *) + (* A `move to' is emitted to start a new contour in an outline. *) + (* *) + (* <Input> *) + (* to :: A pointer to the target point of the `move to'. *) + (* *) + (* user :: A typeless pointer which is passed from the caller of the *) + (* decomposition function. *) + (* *) + (* <Return> *) + (* Error code. 0 means success. *) + (* *) + FT_Outline_MoveToFunc = function(to_: {const} PFT_Vector; + user: Pointer): cint; cdecl; + + + (*************************************************************************) + (* *) + (* <FuncType> *) + (* FT_Outline_LineToFunc *) + (* *) + (* <Description> *) + (* A function pointer type used to describe the signature of a `line *) + (* to' function during outline walking/decomposition. *) + (* *) + (* A `line to' is emitted to indicate a segment in the outline. *) + (* *) + (* <Input> *) + (* to :: A pointer to the target point of the `line to'. *) + (* *) + (* user :: A typeless pointer which is passed from the caller of the *) + (* decomposition function. *) + (* *) + (* <Return> *) + (* Error code. 0 means success. *) + (* *) + FT_Outline_LineToFunc = function(to_: {const} PFT_Vector; + user: Pointer): cint; cdecl; + + + (*************************************************************************) + (* *) + (* <FuncType> *) + (* FT_Outline_ConicToFunc *) + (* *) + (* <Description> *) + (* A function pointer type use to describe the signature of a `conic *) + (* to' function during outline walking/decomposition. *) + (* *) + (* A `conic to' is emitted to indicate a second-order Bézier arc in *) + (* the outline. *) + (* *) + (* <Input> *) + (* control :: An intermediate control point between the last position *) + (* and the new target in `to'. *) + (* *) + (* to :: A pointer to the target end point of the conic arc. *) + (* *) + (* user :: A typeless pointer which is passed from the caller of *) + (* the decomposition function. *) + (* *) + (* <Return> *) + (* Error code. 0 means success. *) + (* *) + FT_Outline_ConicToFunc = function(control: {const} PFT_Vector; + to_: {const} PFT_Vector; + user: Pointer): cint; cdecl; + + + (*************************************************************************) + (* *) + (* <FuncType> *) + (* FT_Outline_CubicToFunc *) + (* *) + (* <Description> *) + (* A function pointer type used to describe the signature of a `cubic *) + (* to' function during outline walking/decomposition. *) + (* *) + (* A `cubic to' is emitted to indicate a third-order Bézier arc. *) + (* *) + (* <Input> *) + (* control1 :: A pointer to the first Bézier control point. *) + (* *) + (* control2 :: A pointer to the second Bézier control point. *) + (* *) + (* to :: A pointer to the target end point. *) + (* *) + (* user :: A typeless pointer which is passed from the caller of *) + (* the decomposition function. *) + (* *) + (* <Return> *) + (* Error code. 0 means success. *) + (* *) + FT_Outline_CubicToFunc = function( control1: {const} PFT_Vector; + control2: {const} PFT_Vector; + to_: {const} PFT_Vector; + user: Pointer ): cint; cdecl; + + + (*************************************************************************) + (* *) + (* <Struct> *) + (* FT_Outline_Funcs *) + (* *) + (* <Description> *) + (* A structure to hold various function pointers used during outline *) + (* decomposition in order to emit segments, conic, and cubic Béziers, *) + (* as well as `move to' and `close to' operations. *) + (* *) + (* <Fields> *) + (* move_to :: The `move to' emitter. *) + (* *) + (* line_to :: The segment emitter. *) + (* *) + (* conic_to :: The second-order Bézier arc emitter. *) + (* *) + (* cubic_to :: The third-order Bézier arc emitter. *) + (* *) + (* shift :: The shift that is applied to coordinates before they *) + (* are sent to the emitter. *) + (* *) + (* delta :: The delta that is applied to coordinates before they *) + (* are sent to the emitter, but after the shift. *) + (* *) + (* <Note> *) + (* The point coordinates sent to the emitters are the transformed *) + (* version of the original coordinates (this is important for high *) + (* accuracy during scan-conversion). The transformation is simple: *) + (* *) + (* { *) + (* x' = (x << shift) - delta *) + (* y' = (x << shift) - delta *) + (* } *) + (* *) + (* Set the value of `shift' and `delta' to 0 to get the original *) + (* point coordinates. *) + (* *) + PFT_Outline_Funcs = ^FT_Outline_Funcs; + FT_Outline_Funcs = record + move_to: FT_Outline_MoveToFunc; + line_to: FT_Outline_LineToFunc; + conic_to: FT_Outline_ConicToFunc; + cubic_to: FT_Outline_CubicToFunc; + + shift: cint; + delta: FT_Pos; + end; + + + (*************************************************************************) + (* *) + (* <Enum> *) + (* FT_Glyph_Format *) + (* *) + (* <Description> *) + (* An enumeration type used to describe the format of a given glyph *) + (* image. Note that this version of FreeType only supports two image *) + (* formats, even though future font drivers will be able to register *) + (* their own format. *) + (* *) + (* <Values> *) + (* FT_GLYPH_FORMAT_NONE :: *) + (* The value 0 is reserved and does describe a glyph format. *) + (* *) + (* FT_GLYPH_FORMAT_COMPOSITE :: *) + (* The glyph image is a composite of several other images. This *) + (* format is _only_ used with @FT_LOAD_NO_RECURSE, and is used to *) + (* report compound glyphs (like accented characters). *) + (* *) + (* FT_GLYPH_FORMAT_BITMAP :: *) + (* The glyph image is a bitmap, and can be described as an *) + (* @FT_Bitmap. You generally need to access the `bitmap' field of *) + (* the @FT_GlyphSlotRec structure to read it. *) + (* *) + (* FT_GLYPH_FORMAT_OUTLINE :: *) + (* The glyph image is a vertorial outline made of line segments *) + (* and Bezier arcs; it can be described as an @FT_Outline; you *) + (* generally want to access the `outline' field of the *) + (* @FT_GlyphSlotRec structure to read it. *) + (* *) + (* FT_GLYPH_FORMAT_PLOTTER :: *) + (* The glyph image is a vectorial path with no inside/outside *) + (* contours. Some Type 1 fonts, like those in the Hershey family, *) + (* contain glyphs in this format. These are described as *) + (* @FT_Outline, but FreeType isn't currently capable of rendering *) + (* them correctly. *) + (* *) + FT_Glyph_Format = array[0..3] of char; +{$ELSE TYPE_DECL} +const + FT_GLYPH_FORMAT_NONE: FT_Glyph_Format = (#0, #0, #0, #0 ); + + FT_GLYPH_FORMAT_COMPOSITE: FT_Glyph_Format = ('c', 'o', 'm', 'p' ); + FT_GLYPH_FORMAT_BITMAP: FT_Glyph_Format = ('b', 'i', 't', 's' ); + FT_GLYPH_FORMAT_OUTLINE: FT_Glyph_Format = ('o', 'u', 't', 'l' ); + FT_GLYPH_FORMAT_PLOTTER: FT_Glyph_Format = ('p', 'l', 'o', 't' ); + +{$ENDIF TYPE_DECL} + + (*************************************************************************) + (*************************************************************************) + (*************************************************************************) + (***** *****) + (***** R A S T E R D E F I N I T I O N S *****) + (***** *****) + (*************************************************************************) + (*************************************************************************) + (*************************************************************************) + + + (*************************************************************************) + (* *) + (* A raster is a scan converter, in charge of rendering an outline into *) + (* a a bitmap. This section contains the public API for rasters. *) + (* *) + (* Note that in FreeType 2, all rasters are now encapsulated within *) + (* specific modules called `renderers'. See `freetype/ftrender.h' for *) + (* more details on renderers. *) + (* *) + (*************************************************************************) + + + (*************************************************************************) + (* *) + (* <Section> *) + (* raster *) + (* *) + (* <Title> *) + (* Scanline Converter *) + (* *) + (* <Abstract> *) + (* How vectorial outlines are converted into bitmaps and pixmaps. *) + (* *) + (* <Description> *) + (* This section contains technical definitions. *) + (* *) + (*************************************************************************) + +{$IFDEF TYPE_DECL} + + (*************************************************************************) + (* *) + (* <Type> *) + (* FT_Raster *) + (* *) + (* <Description> *) + (* A handle (pointer) to a raster object. Each object can be used *) + (* independently to convert an outline into a bitmap or pixmap. *) + (* *) + FT_Raster = Pointer; + + + (*************************************************************************) + (* *) + (* <Struct> *) + (* FT_Span *) + (* *) + (* <Description> *) + (* A structure used to model a single span of gray (or black) pixels *) + (* when rendering a monochrome or anti-aliased bitmap. *) + (* *) + (* <Fields> *) + (* x :: The span's horizontal start position. *) + (* *) + (* len :: The span's length in pixels. *) + (* *) + (* coverage :: The span color/coverage, ranging from 0 (background) *) + (* to 255 (foreground). Only used for anti-aliased *) + (* rendering. *) + (* *) + (* <Note> *) + (* This structure is used by the span drawing callback type named *) + (* @FT_SpanFunc which takes the y-coordinate of the span as a *) + (* a parameter. *) + (* *) + (* The coverage value is always between 0 and 255. *) + (* *) + PFT_Span = ^FT_Span; + FT_Span = record + x: cshort; + len: cushort; + coverage: cuchar; + end; + + + (*************************************************************************) + (* *) + (* <FuncType> *) + (* FT_SpanFunc *) + (* *) + (* <Description> *) + (* A function used as a call-back by the anti-aliased renderer in *) + (* order to let client applications draw themselves the gray pixel *) + (* spans on each scan line. *) + (* *) + (* <Input> *) + (* y :: The scanline's y-coordinate. *) + (* *) + (* count :: The number of spans to draw on this scanline. *) + (* *) + (* spans :: A table of `count' spans to draw on the scanline. *) + (* *) + (* user :: User-supplied data that is passed to the callback. *) + (* *) + (* <Note> *) + (* This callback allows client applications to directly render the *) + (* gray spans of the anti-aliased bitmap to any kind of surfaces. *) + (* *) + (* This can be used to write anti-aliased outlines directly to a *) + (* given background bitmap, and even perform translucency. *) + (* *) + (* Note that the `count' field cannot be greater than a fixed value *) + (* defined by the `FT_MAX_GRAY_SPANS' configuration macro in *) + (* `ftoption.h'. By default, this value is set to 32, which means *) + (* that if there are more than 32 spans on a given scanline, the *) + (* callback is called several times with the same `y' parameter in *) + (* order to draw all callbacks. *) + (* *) + (* Otherwise, the callback is only called once per scan-line, and *) + (* only for those scanlines that do have `gray' pixels on them. *) + (* *) + FT_SpanFunc = procedure(y: cint; + count: cint; + spans: {const} PFT_Span; + user: Pointer ); cdecl; + + + (*************************************************************************) + (* *) + (* <FuncType> *) + (* FT_Raster_BitTest_Func *) + (* *) + (* <Description> *) + (* THIS TYPE IS DEPRECATED. DO NOT USE IT. *) + (* *) + (* A function used as a call-back by the monochrome scan-converter *) + (* to test whether a given target pixel is already set to the drawing *) + (* `color'. These tests are crucial to implement drop-out control *) + (* per-se the TrueType spec. *) + (* *) + (* <Input> *) + (* y :: The pixel's y-coordinate. *) + (* *) + (* x :: The pixel's x-coordinate. *) + (* *) + (* user :: User-supplied data that is passed to the callback. *) + (* *) + (* <Return> *) + (* 1 if the pixel is `set', 0 otherwise. *) + (* *) + FT_Raster_BitTest_Func = function(y: cint; + x: cint; + user: Pointer): cint; cdecl; + + + (*************************************************************************) + (* *) + (* <FuncType> *) + (* FT_Raster_BitSet_Func *) + (* *) + (* <Description> *) + (* THIS TYPE IS DEPRECATED. DO NOT USE IT. *) + (* *) + (* A function used as a call-back by the monochrome scan-converter *) + (* to set an individual target pixel. This is crucial to implement *) + (* drop-out control according to the TrueType specification. *) + (* *) + (* <Input> *) + (* y :: The pixel's y-coordinate. *) + (* *) + (* x :: The pixel's x-coordinate. *) + (* *) + (* user :: User-supplied data that is passed to the callback. *) + (* *) + (* <Return> *) + (* 1 if the pixel is `set', 0 otherwise. *) + (* *) + FT_Raster_BitSet_Func = procedure(y: cint; + x: cint; + user: Pointer ); cdecl; + + + (*************************************************************************) + (* *) + (* <Enum> *) + (* FT_RASTER_FLAG_XXX *) + (* *) + (* <Description> *) + (* A list of bit flag constants as used in the `flags' field of a *) + (* @FT_Raster_Params structure. *) + (* *) + (* <Values> *) + (* FT_RASTER_FLAG_DEFAULT :: This value is 0. *) + (* *) + (* FT_RASTER_FLAG_AA :: This flag is set to indicate that an *) + (* anti-aliased glyph image should be *) + (* generated. Otherwise, it will be *) + (* monochrome (1-bit). *) + (* *) + (* FT_RASTER_FLAG_DIRECT :: This flag is set to indicate direct *) + (* rendering. In this mode, client *) + (* applications must provide their own span *) + (* callback. This lets them directly *) + (* draw or compose over an existing bitmap. *) + (* If this bit is not set, the target *) + (* pixmap's buffer _must_ be zeroed before *) + (* rendering. *) + (* *) + (* Note that for now, direct rendering is *) + (* only possible with anti-aliased glyphs. *) + (* *) + (* FT_RASTER_FLAG_CLIP :: This flag is only used in direct *) + (* rendering mode. If set, the output will *) + (* be clipped to a box specified in the *) + (* `clip_box' field of the *) + (* @FT_Raster_Params structure. *) + (* *) + (* Note that by default, the glyph bitmap *) + (* is clipped to the target pixmap, except *) + (* in direct rendering mode where all spans *) + (* are generated if no clipping box is set. *) + (* *) +{$ELSE TYPE_DECL} +const + FT_RASTER_FLAG_DEFAULT = $0; + FT_RASTER_FLAG_AA = $1; + FT_RASTER_FLAG_DIRECT = $2; + FT_RASTER_FLAG_CLIP = $4; + +{$ENDIF TYPE_DECL} +{$IFDEF TYPE_DECL} + + (*************************************************************************) + (* *) + (* <Struct> *) + (* FT_Raster_Params *) + (* *) + (* <Description> *) + (* A structure to hold the arguments used by a raster's render *) + (* function. *) + (* *) + (* <Fields> *) + (* target :: The target bitmap. *) + (* *) + (* source :: A pointer to the source glyph image (e.g., an *) + (* @FT_Outline). *) + (* *) + (* flags :: The rendering flags. *) + (* *) + (* gray_spans :: The gray span drawing callback. *) + (* *) + (* black_spans :: The black span drawing callback. *) + (* *) + (* bit_test :: The bit test callback. UNIMPLEMENTED! *) + (* *) + (* bit_set :: The bit set callback. UNIMPLEMENTED! *) + (* *) + (* user :: User-supplied data that is passed to each drawing *) + (* callback. *) + (* *) + (* clip_box :: An optional clipping box. It is only used in *) + (* direct rendering mode. Note that coordinates here *) + (* should be expressed in _integer_ pixels (and not in *) + (* 26.6 fixed-point units). *) + (* *) + (* <Note> *) + (* An anti-aliased glyph bitmap is drawn if the @FT_RASTER_FLAG_AA *) + (* bit flag is set in the `flags' field, otherwise a monochrome *) + (* bitmap is generated. *) + (* *) + (* If the @FT_RASTER_FLAG_DIRECT bit flag is set in `flags', the *) + (* raster will call the `gray_spans' callback to draw gray pixel *) + (* spans, in the case of an aa glyph bitmap, it will call *) + (* `black_spans', and `bit_test' and `bit_set' in the case of a *) + (* monochrome bitmap. This allows direct composition over a *) + (* pre-existing bitmap through user-provided callbacks to perform the *) + (* span drawing/composition. *) + (* *) + (* Note that the `bit_test' and `bit_set' callbacks are required when *) + (* rendering a monochrome bitmap, as they are crucial to implement *) + (* correct drop-out control as defined in the TrueType specification. *) + (* *) + PFT_Raster_Params = ^FT_Raster_Params; + FT_Raster_Params = record + target: {const} PFT_Bitmap; + source: {const} Pointer; + flags: cint; + gray_spans: FT_SpanFunc; + black_spans: FT_SpanFunc; + bit_test: FT_Raster_BitTest_Func; (* doesn't work! *) + bit_set: FT_Raster_BitSet_Func; (* doesn't work! *) + user: Pointer; + clip_box: FT_BBox; + end; + +{$ENDIF TYPE_DECL} + + diff --git a/Lua/src/lib/freetype/ftoutln.inc b/Lua/src/lib/freetype/ftoutln.inc new file mode 100644 index 00000000..997c6cb3 --- /dev/null +++ b/Lua/src/lib/freetype/ftoutln.inc @@ -0,0 +1,497 @@ +(***************************************************************************) +(* *) +(* ftoutln.h *) +(* *) +(* Support for the FT_Outline type used to store glyph shapes of *) +(* most scalable font formats (specification). *) +(* *) +(* Copyright 1996-2001, 2002, 2003, 2005, 2006, 2007 by *) +(* David Turner, Robert Wilhelm, and Werner Lemberg. *) +(* *) +(* This file is part of the FreeType project, and may only be used, *) +(* modified, and distributed under the terms of the FreeType project *) +(* license, LICENSE.TXT. By continuing to use, modify, or distribute *) +(* this file you indicate that you have read the license and *) +(* understand and accept it fully. *) +(* *) +(***************************************************************************) +(***************************************************************************) +(* Pascal port by the UltraStar Deluxe Team *) +(***************************************************************************) + + + (*************************************************************************) + (* *) + (* <Section> *) + (* outline_processing *) + (* *) + (* <Title> *) + (* Outline Processing *) + (* *) + (* <Abstract> *) + (* Functions to create, transform, and render vectorial glyph images. *) + (* *) + (* <Description> *) + (* This section contains routines used to create and destroy scalable *) + (* glyph images known as `outlines'. These can also be measured, *) + (* transformed, and converted into bitmaps and pixmaps. *) + (* *) + (* <Order> *) + (* FT_Outline *) + (* FT_OUTLINE_FLAGS *) + (* FT_Outline_New *) + (* FT_Outline_Done *) + (* FT_Outline_Copy *) + (* FT_Outline_Translate *) + (* FT_Outline_Transform *) + (* FT_Outline_Embolden *) + (* FT_Outline_Reverse *) + (* FT_Outline_Check *) + (* *) + (* FT_Outline_Get_CBox *) + (* FT_Outline_Get_BBox *) + (* *) + (* FT_Outline_Get_Bitmap *) + (* FT_Outline_Render *) + (* *) + (* FT_Outline_Decompose *) + (* FT_Outline_Funcs *) + (* FT_Outline_MoveTo_Func *) + (* FT_Outline_LineTo_Func *) + (* FT_Outline_ConicTo_Func *) + (* FT_Outline_CubicTo_Func *) + (* *) + (*************************************************************************) + +{$IFNDEF TYPE_DECL} + + (*************************************************************************) + (* *) + (* <Function> *) + (* FT_Outline_Decompose *) + (* *) + (* <Description> *) + (* Walks over an outline's structure to decompose it into individual *) + (* segments and Bézier arcs. This function is also able to emit *) + (* `move to' and `close to' operations to indicate the start and end *) + (* of new contours in the outline. *) + (* *) + (* <Input> *) + (* outline :: A pointer to the source target. *) + (* *) + (* func_interface :: A table of `emitters', i.e,. function pointers *) + (* called during decomposition to indicate path *) + (* operations. *) + (* *) + (* <InOut> *) + (* user :: A typeless pointer which is passed to each *) + (* emitter during the decomposition. It can be *) + (* used to store the state during the *) + (* decomposition. *) + (* *) + (* <Return> *) + (* FreeType error code. 0 means success. *) + (* *) + function FT_Outline_Decompose( + outline: PFT_Outline; + func_interface: {const} PFT_Outline_Funcs; + user: Pointer): FT_Error; + cdecl; external ft_lib name 'FT_Outline_Decompose'; + + + (*************************************************************************) + (* *) + (* <Function> *) + (* FT_Outline_New *) + (* *) + (* <Description> *) + (* Creates a new outline of a given size. *) + (* *) + (* <Input> *) + (* library :: A handle to the library object from where the *) + (* outline is allocated. Note however that the new *) + (* outline will *not* necessarily be *freed*, when *) + (* destroying the library, by @FT_Done_FreeType. *) + (* *) + (* numPoints :: The maximal number of points within the outline. *) + (* *) + (* numContours :: The maximal number of contours within the outline. *) + (* *) + (* <Output> *) + (* anoutline :: A handle to the new outline. NULL in case of *) + (* error. *) + (* *) + (* <Return> *) + (* FreeType error code. 0 means success. *) + (* *) + (* <Note> *) + (* The reason why this function takes a `library' parameter is simply *) + (* to use the library's memory allocator. *) + (* *) + function FT_Outline_New( + library_: FT_Library; + numPoints: FT_UInt; + numContours: FT_Int; + anoutline: PFT_Outline): FT_Error; + cdecl; external ft_lib name 'FT_Outline_New'; + + + (*************************************************************************) + (* *) + (* <Function> *) + (* FT_Outline_Done *) + (* *) + (* <Description> *) + (* Destroys an outline created with @FT_Outline_New. *) + (* *) + (* <Input> *) + (* library :: A handle of the library object used to allocate the *) + (* outline. *) + (* *) + (* outline :: A pointer to the outline object to be discarded. *) + (* *) + (* <Return> *) + (* FreeType error code. 0 means success. *) + (* *) + (* <Note> *) + (* If the outline's `owner' field is not set, only the outline *) + (* descriptor will be released. *) + (* *) + (* The reason why this function takes an `library' parameter is *) + (* simply to use ft_mem_free(). *) + (* *) + function FT_Outline_Done(library_: FT_Library; + outline: PFT_Outline): FT_Error; + cdecl; external ft_lib name 'FT_Outline_Done'; + + + (*************************************************************************) + (* *) + (* <Function> *) + (* FT_Outline_Check *) + (* *) + (* <Description> *) + (* Check the contents of an outline descriptor. *) + (* *) + (* <Input> *) + (* outline :: A handle to a source outline. *) + (* *) + (* <Return> *) + (* FreeType error code. 0 means success. *) + (* *) + function FT_Outline_Check( outline: PFT_Outline ): FT_Error; + cdecl; external ft_lib name 'FT_Outline_Check'; + + + (*************************************************************************) + (* *) + (* <Function> *) + (* FT_Outline_Get_CBox *) + (* *) + (* <Description> *) + (* Returns an outline's `control box'. The control box encloses all *) + (* the outline's points, including Bézier control points. Though it *) + (* coincides with the exact bounding box for most glyphs, it can be *) + (* slightly larger in some situations (like when rotating an outline *) + (* which contains Bézier outside arcs). *) + (* *) + (* Computing the control box is very fast, while getting the bounding *) + (* box can take much more time as it needs to walk over all segments *) + (* and arcs in the outline. To get the latter, you can use the *) + (* `ftbbox' component which is dedicated to this single task. *) + (* *) + (* <Input> *) + (* outline :: A pointer to the source outline descriptor. *) + (* *) + (* <Output> *) + (* acbox :: The outline's control box. *) + (* *) + procedure FT_Outline_Get_CBox( + outline: {const} PFT_Outline; + acbox: PFT_BBox); + cdecl; external ft_lib name 'FT_Outline_Get_CBox'; + + + (*************************************************************************) + (* *) + (* <Function> *) + (* FT_Outline_Translate *) + (* *) + (* <Description> *) + (* Applies a simple translation to the points of an outline. *) + (* *) + (* <InOut> *) + (* outline :: A pointer to the target outline descriptor. *) + (* *) + (* <Input> *) + (* xOffset :: The horizontal offset. *) + (* *) + (* yOffset :: The vertical offset. *) + (* *) + procedure FT_Outline_Translate( + outline: {const} PFT_Outline; + xOffset: FT_Pos; + yOffset: FT_Pos); + cdecl; external ft_lib name 'FT_Outline_Translate'; + + + (*************************************************************************) + (* *) + (* <Function> *) + (* FT_Outline_Copy *) + (* *) + (* <Description> *) + (* Copies an outline into another one. Both objects must have the *) + (* same sizes (number of points & number of contours) when this *) + (* function is called. *) + (* *) + (* <Input> *) + (* source :: A handle to the source outline. *) + (* *) + (* <Output> *) + (* target :: A handle to the target outline. *) + (* *) + (* <Return> *) + (* FreeType error code. 0 means success. *) + (* *) + function FT_Outline_Copy( + source: {const} PFT_Outline; + target: PFT_Outline): FT_Error; + cdecl; external ft_lib name 'FT_Outline_Copy'; + + + (*************************************************************************) + (* *) + (* <Function> *) + (* FT_Outline_Transform *) + (* *) + (* <Description> *) + (* Applies a simple 2x2 matrix to all of an outline's points. Useful *) + (* for applying rotations, slanting, flipping, etc. *) + (* *) + (* <InOut> *) + (* outline :: A pointer to the target outline descriptor. *) + (* *) + (* <Input> *) + (* matrix :: A pointer to the transformation matrix. *) + (* *) + (* <Note> *) + (* You can use @FT_Outline_Translate if you need to translate the *) + (* outline's points. *) + (* *) + procedure FT_Outline_Transform( + outline: {const} PFT_Outline; + matrix: {const} PFT_Matrix); + cdecl; external ft_lib name 'FT_Outline_Transform'; + + + (*************************************************************************) + (* *) + (* <Function> *) + (* FT_Outline_Embolden *) + (* *) + (* <Description> *) + (* Emboldens an outline. The new outline will be at most 4 times *) + (* `strength' pixels wider and higher. You may think of the left and *) + (* bottom borders as unchanged. *) + (* *) + (* Negative `strength' values to reduce the outline thickness are *) + (* possible also. *) + (* *) + (* <InOut> *) + (* outline :: A handle to the target outline. *) + (* *) + (* <Input> *) + (* strength :: How strong the glyph is emboldened. Expressed in *) + (* 26.6 pixel format. *) + (* *) + (* <Return> *) + (* FreeType error code. 0 means success. *) + (* *) + (* <Note> *) + (* The used algorithm to increase or decrease the thickness of the *) + (* glyph doesn't change the number of points; this means that certain *) + (* situations like acute angles or intersections are sometimes *) + (* handled incorrectly. *) + (* *) + (* Example call: *) + (* *) + (* { *) + (* FT_Load_Glyph( face, index, FT_LOAD_DEFAULT ); *) + (* if ( face->slot->format == FT_GLYPH_FORMAT_OUTLINE ) *) + (* FT_Outline_Embolden( &face->slot->outline, strength ); *) + (* } *) + (* *) + function FT_Outline_Embolden( + outline: PFT_Outline; + strength: FT_Pos): FT_Error; + cdecl; external ft_lib name 'FT_Outline_Embolden'; + + + (*************************************************************************) + (* *) + (* <Function> *) + (* FT_Outline_Reverse *) + (* *) + (* <Description> *) + (* Reverses the drawing direction of an outline. This is used to *) + (* ensure consistent fill conventions for mirrored glyphs. *) + (* *) + (* <InOut> *) + (* outline :: A pointer to the target outline descriptor. *) + (* *) + (* <Note> *) + (* This functions toggles the bit flag @FT_OUTLINE_REVERSE_FILL in *) + (* the outline's `flags' field. *) + (* *) + (* It shouldn't be used by a normal client application, unless it *) + (* knows what it is doing. *) + (* *) + procedure FT_Outline_Reverse( outline: PFT_Outline ); + cdecl; external ft_lib name 'FT_Outline_Reverse'; + + + (*************************************************************************) + (* *) + (* <Function> *) + (* FT_Outline_Get_Bitmap *) + (* *) + (* <Description> *) + (* Renders an outline within a bitmap. The outline's image is simply *) + (* OR-ed to the target bitmap. *) + (* *) + (* <Input> *) + (* library :: A handle to a FreeType library object. *) + (* *) + (* outline :: A pointer to the source outline descriptor. *) + (* *) + (* <InOut> *) + (* abitmap :: A pointer to the target bitmap descriptor. *) + (* *) + (* <Return> *) + (* FreeType error code. 0 means success. *) + (* *) + (* <Note> *) + (* This function does NOT CREATE the bitmap, it only renders an *) + (* outline image within the one you pass to it! *) + (* *) + (* It will use the raster corresponding to the default glyph format. *) + (* *) + function FT_Outline_Get_Bitmap( + library_: FT_Library; + outline: PFT_Outline; + abitmap: {const} PFT_Bitmap): FT_Error; + cdecl; external ft_lib name 'FT_Outline_Get_Bitmap'; + + + (*************************************************************************) + (* *) + (* <Function> *) + (* FT_Outline_Render *) + (* *) + (* <Description> *) + (* Renders an outline within a bitmap using the current scan-convert. *) + (* This functions uses an @FT_Raster_Params structure as an argument, *) + (* allowing advanced features like direct composition, translucency, *) + (* etc. *) + (* *) + (* <Input> *) + (* library :: A handle to a FreeType library object. *) + (* *) + (* outline :: A pointer to the source outline descriptor. *) + (* *) + (* <InOut> *) + (* params :: A pointer to an @FT_Raster_Params structure used to *) + (* describe the rendering operation. *) + (* *) + (* <Return> *) + (* FreeType error code. 0 means success. *) + (* *) + (* <Note> *) + (* You should know what you are doing and how @FT_Raster_Params works *) + (* to use this function. *) + (* *) + (* The field `params.source' will be set to `outline' before the scan *) + (* converter is called, which means that the value you give to it is *) + (* actually ignored. *) + (* *) + function FT_Outline_Render( + library_: FT_Library; + outline: PFT_Outline; + params: PFT_Raster_Params): FT_Error; + cdecl; external ft_lib name 'FT_Outline_Render'; + +{$ENDIF TYPE_DECL} + + (************************************************************************** + * + * @enum: + * FT_Orientation + * + * @description: + * A list of values used to describe an outline's contour orientation. + * + * The TrueType and Postscript specifications use different conventions + * to determine whether outline contours should be filled or unfilled. + * + * @values: + * FT_ORIENTATION_TRUETYPE :: + * According to the TrueType specification, clockwise contours must + * be filled, and counter-clockwise ones must be unfilled. + * + * FT_ORIENTATION_POSTSCRIPT :: + * According to the Postscript specification, counter-clockwise contours + * must be filled, and clockwise ones must be unfilled. + * + * FT_ORIENTATION_FILL_RIGHT :: + * This is identical to @FT_ORIENTATION_TRUETYPE, but is used to + * remember that in TrueType, everything that is to the right of + * the drawing direction of a contour must be filled. + * + * FT_ORIENTATION_FILL_LEFT :: + * This is identical to @FT_ORIENTATION_POSTSCRIPT, but is used to + * remember that in Postscript, everything that is to the left of + * the drawing direction of a contour must be filled. + * + * FT_ORIENTATION_NONE :: + * The orientation cannot be determined. That is, different parts of + * the glyph have different orientation. + * + *) +{$IFDEF TYPE_DECL} + FT_Orientation = cint; +{$ELSE TYPE_DECL} +const + FT_ORIENTATION_TRUETYPE = 0; + FT_ORIENTATION_POSTSCRIPT = 1; + FT_ORIENTATION_FILL_RIGHT = FT_ORIENTATION_TRUETYPE; + FT_ORIENTATION_FILL_LEFT = FT_ORIENTATION_POSTSCRIPT; + FT_ORIENTATION_NONE = FT_ORIENTATION_FILL_LEFT+1; + + (************************************************************************** + * + * @function: + * FT_Outline_Get_Orientation + * + * @description: + * This function analyzes a glyph outline and tries to compute its + * fill orientation (see @FT_Orientation). This is done by computing + * the direction of each global horizontal and/or vertical extrema + * within the outline. + * + * Note that this will return @FT_ORIENTATION_TRUETYPE for empty + * outlines. + * + * @input: + * outline :: + * A handle to the source outline. + * + * @return: + * The orientation. + * + *) + function FT_Outline_Get_Orientation( outline: PFT_Outline ): FT_Orientation; + cdecl; external ft_lib name 'FT_Outline_Get_Orientation'; + +{$ENDIF TYPE_DECL} + diff --git a/Lua/src/lib/freetype/ftstroke.inc b/Lua/src/lib/freetype/ftstroke.inc new file mode 100644 index 00000000..bf8a00ae --- /dev/null +++ b/Lua/src/lib/freetype/ftstroke.inc @@ -0,0 +1,711 @@ +{***************************************************************************} +{* *} +{* ftstroke.h *} +{* *} +{* FreeType path stroker (specification). *} +{* *} +{* Copyright 2002, 2003, 2004, 2005, 2006 by *} +{* David Turner, Robert Wilhelm, and Werner Lemberg. *} +{* *} +{* This file is part of the FreeType project, and may only be used, *} +{* modified, and distributed under the terms of the FreeType project *} +{* license, LICENSE.TXT. By continuing to use, modify, or distribute *} +{* this file you indicate that you have read the license and *} +{* understand and accept it fully. *} +{* *} +{***************************************************************************} +(***************************************************************************) +(* Pascal port by the UltraStar Deluxe Team *) +(***************************************************************************) + + {************************************************************************ + * + * @section: + * glyph_stroker + * + * @title: + * Glyph Stroker + * + * @abstract: + * Generating bordered and stroked glyphs. + * + * @description: + * This component generates stroked outlines of a given vectorial + * glyph. It also allows you to retrieve the `outside' and/or the + * `inside' borders of the stroke. + * + * This can be useful to generate `bordered' glyph, i.e., glyphs + * displayed with a coloured (and anti-aliased) border around their + * shape. + *} + +{$IFDEF TYPE_DECL} + + {************************************************************** + * + * @type: + * FT_Stroker + * + * @description: + * Opaque handler to a path stroker object. + *} + FT_Stroker = Pointer; + + + {************************************************************** + * + * @enum: + * FT_Stroker_LineJoin + * + * @description: + * These values determine how two joining lines are rendered + * in a stroker. + * + * @values: + * FT_STROKER_LINEJOIN_ROUND :: + * Used to render rounded line joins. Circular arcs are used + * to join two lines smoothly. + * + * FT_STROKER_LINEJOIN_BEVEL :: + * Used to render beveled line joins; i.e., the two joining lines + * are extended until they intersect. + * + * FT_STROKER_LINEJOIN_MITER :: + * Same as beveled rendering, except that an additional line + * break is added if the angle between the two joining lines + * is too closed (this is useful to avoid unpleasant spikes + * in beveled rendering). + *} + FT_Stroker_LineJoin = cint; +{$ELSE TYPE_DECL} +const + FT_STROKER_LINEJOIN_ROUND = 0; + FT_STROKER_LINEJOIN_BEVEL = 1; + FT_STROKER_LINEJOIN_MITER = 2; + +{$ENDIF TYPE_DECL} +{$IFDEF TYPE_DECL} + + {************************************************************** + * + * @enum: + * FT_Stroker_LineCap + * + * @description: + * These values determine how the end of opened sub-paths are + * rendered in a stroke. + * + * @values: + * FT_STROKER_LINECAP_BUTT :: + * The end of lines is rendered as a full stop on the last + * point itself. + * + * FT_STROKER_LINECAP_ROUND :: + * The end of lines is rendered as a half-circle around the + * last point. + * + * FT_STROKER_LINECAP_SQUARE :: + * The end of lines is rendered as a square around the + * last point. + *} + FT_Stroker_LineCap = cint; +{$ELSE TYPE_DECL} +const + FT_STROKER_LINECAP_BUTT = 0; + FT_STROKER_LINECAP_ROUND = 1; + FT_STROKER_LINECAP_SQUARE = 2; + +{$ENDIF TYPE_DECL} +{$IFDEF TYPE_DECL} + + {************************************************************** + * + * @enum: + * FT_StrokerBorder + * + * @description: + * These values are used to select a given stroke border + * in @FT_Stroker_GetBorderCounts and @FT_Stroker_ExportBorder. + * + * @values: + * FT_STROKER_BORDER_LEFT :: + * Select the left border, relative to the drawing direction. + * + * FT_STROKER_BORDER_RIGHT :: + * Select the right border, relative to the drawing direction. + * + * @note: + * Applications are generally interested in the `inside' and `outside' + * borders. However, there is no direct mapping between these and the + * `left' and `right' ones, since this really depends on the glyph's + * drawing orientation, which varies between font formats. + * + * You can however use @FT_Outline_GetInsideBorder and + * @FT_Outline_GetOutsideBorder to get these. + *} + FT_StrokerBorder = cint; +{$ELSE TYPE_DECL} +const + FT_STROKER_BORDER_LEFT = 0; + FT_STROKER_BORDER_RIGHT = 1; + + + {************************************************************** + * + * @function: + * FT_Outline_GetInsideBorder + * + * @description: + * Retrieve the @FT_StrokerBorder value corresponding to the + * `inside' borders of a given outline. + * + * @input: + * outline :: + * The source outline handle. + * + * @return: + * The border index. @FT_STROKER_BORDER_LEFT for empty or invalid + * outlines. + *} + function FT_Outline_GetInsideBorder( outline: PFT_Outline ): FT_StrokerBorder; + cdecl; external ft_lib name 'FT_Outline_GetInsideBorder'; + + + {************************************************************** + * + * @function: + * FT_Outline_GetOutsideBorder + * + * @description: + * Retrieve the @FT_StrokerBorder value corresponding to the + * `outside' borders of a given outline. + * + * @input: + * outline :: + * The source outline handle. + * + * @return: + * The border index. @FT_STROKER_BORDER_LEFT for empty or invalid + * outlines. + *} + function FT_Outline_GetOutsideBorder( outline: PFT_Outline ): FT_StrokerBorder; + cdecl; external ft_lib name 'FT_Outline_GetOutsideBorder'; + + + {************************************************************** + * + * @function: + * FT_Stroker_New + * + * @description: + * Create a new stroker object. + * + * @input: + * library :: + * FreeType library handle. + * + * @output: + * astroker :: + * A new stroker object handle. NULL in case of error. + * + * @return: + * FreeType error code. 0 means success. + *} + function FT_Stroker_New( + library_: FT_Library; + out astroker: FT_Stroker ): FT_Error; + cdecl; external ft_lib name 'FT_Stroker_New'; + + + {************************************************************** + * + * @function: + * FT_Stroker_Set + * + * @description: + * Reset a stroker object's attributes. + * + * @input: + * stroker :: + * The target stroker handle. + * + * radius :: + * The border radius. + * + * line_cap :: + * The line cap style. + * + * line_join :: + * The line join style. + * + * miter_limit :: + * The miter limit for the FT_STROKER_LINEJOIN_MITER style, + * expressed as 16.16 fixed point value. + * + * @note: + * The radius is expressed in the same units that the outline + * coordinates. + *} + procedure FT_Stroker_Set( + stroker: FT_Stroker; + radius: FT_Fixed; + line_cap: FT_Stroker_LineCap; + line_join: FT_Stroker_LineJoin; + miter_limit: FT_Fixed ); + cdecl; external ft_lib name 'FT_Stroker_Set'; + + + {************************************************************** + * + * @function: + * FT_Stroker_Rewind + * + * @description: + * Reset a stroker object without changing its attributes. + * You should call this function before beginning a new + * series of calls to @FT_Stroker_BeginSubPath or + * @FT_Stroker_EndSubPath. + * + * @input: + * stroker :: + * The target stroker handle. + *} + procedure FT_Stroker_Rewind( stroker: FT_Stroker ); + cdecl; external ft_lib name 'FT_Stroker_Rewind'; + + + {************************************************************** + * + * @function: + * FT_Stroker_ParseOutline + * + * @description: + * A convenience function used to parse a whole outline with + * the stroker. The resulting outline(s) can be retrieved + * later by functions like @FT_Stroker_GetCounts and @FT_Stroker_Export. + * + * @input: + * stroker :: + * The target stroker handle. + * + * outline :: + * The source outline. + * + * opened :: + * A boolean. If 1, the outline is treated as an open path instead + * of a closed one. + * + * @return: + * FreeType error code. 0 means success. + * + * @note: + * If `opened' is 0 (the default), the outline is treated as a closed + * path, and the stroker will generate two distinct `border' outlines. + * + * If `opened' is 1, the outline is processed as an open path, and the + * stroker will generate a single `stroke' outline. + * + * This function calls @FT_Stroker_Rewind automatically. + *} + function FT_Stroker_ParseOutline( + stroker: FT_Stroker; + outline: PFT_Outline; + opened: FT_Bool): FT_Error; + cdecl; external ft_lib name 'FT_Stroker_ParseOutline'; + + + {************************************************************** + * + * @function: + * FT_Stroker_BeginSubPath + * + * @description: + * Start a new sub-path in the stroker. + * + * @input: + * stroker :: + * The target stroker handle. + * + * to :: + * A pointer to the start vector. + * + * open :: + * A boolean. If 1, the sub-path is treated as an open one. + * + * @return: + * FreeType error code. 0 means success. + * + * @note: + * This function is useful when you need to stroke a path that is + * not stored as an @FT_Outline object. + *} + function FT_Stroker_BeginSubPath( + stroker: FT_Stroker; + to_: PFT_Vector; + open: FT_Bool ): FT_Error; + cdecl; external ft_lib name 'FT_Stroker_BeginSubPath'; + + + {************************************************************** + * + * @function: + * FT_Stroker_EndSubPath + * + * @description: + * Close the current sub-path in the stroker. + * + * @input: + * stroker :: + * The target stroker handle. + * + * @return: + * FreeType error code. 0 means success. + * + * @note: + * You should call this function after @FT_Stroker_BeginSubPath. + * If the subpath was not `opened', this function will `draw' a + * single line segment to the start position when needed. + *} + function FT_Stroker_EndSubPath( stroker: FT_Stroker ): FT_Error; + cdecl; external ft_lib name 'FT_Stroker_EndSubPath'; + + + {************************************************************** + * + * @function: + * FT_Stroker_LineTo + * + * @description: + * `Draw' a single line segment in the stroker's current sub-path, + * from the last position. + * + * @input: + * stroker :: + * The target stroker handle. + * + * to :: + * A pointer to the destination point. + * + * @return: + * FreeType error code. 0 means success. + * + * @note: + * You should call this function between @FT_Stroker_BeginSubPath and + * @FT_Stroker_EndSubPath. + *} + function FT_Stroker_LineTo( + stroker: FT_Stroker; + to_: PFT_Vector ): FT_Error; + cdecl; external ft_lib name 'FT_Stroker_LineTo'; + + + {************************************************************** + * + * @function: + * FT_Stroker_ConicTo + * + * @description: + * `Draw' a single quadratic Bézier in the stroker's current sub-path, + * from the last position. + * + * @input: + * stroker :: + * The target stroker handle. + * + * control :: + * A pointer to a Bézier control point. + * + * to :: + * A pointer to the destination point. + * + * @return: + * FreeType error code. 0 means success. + * + * @note: + * You should call this function between @FT_Stroker_BeginSubPath and + * @FT_Stroker_EndSubPath. + *} + function FT_Stroker_ConicTo( + stroker: FT_Stroker; + control: PFT_Vector; + to_: PFT_Vector ): FT_Error; + cdecl; external ft_lib name 'FT_Stroker_ConicTo'; + + + {************************************************************** + * + * @function: + * FT_Stroker_CubicTo + * + * @description: + * `Draw' a single cubic Bézier in the stroker's current sub-path, + * from the last position. + * + * @input: + * stroker :: + * The target stroker handle. + * + * control1 :: + * A pointer to the first Bézier control point. + * + * control2 :: + * A pointer to second Bézier control point. + * + * to :: + * A pointer to the destination point. + * + * @return: + * FreeType error code. 0 means success. + * + * @note: + * You should call this function between @FT_Stroker_BeginSubPath and + * @FT_Stroker_EndSubPath. + *} + function FT_Stroker_CubicTo( + stroker: FT_Stroker; + control1: PFT_Vector; + control2: PFT_Vector; + to_: PFT_Vector ): FT_Error; + cdecl; external ft_lib name 'FT_Stroker_CubicTo'; + + + {************************************************************** + * + * @function: + * FT_Stroker_GetBorderCounts + * + * @description: + * Call this function once you have finished parsing your paths + * with the stroker. It will return the number of points and + * contours necessary to export one of the `border' or `stroke' + * outlines generated by the stroker. + * + * @input: + * stroker :: + * The target stroker handle. + * + * border :: + * The border index. + * + * @output: + * anum_points :: + * The number of points. + * + * anum_contours :: + * The number of contours. + * + * @return: + * FreeType error code. 0 means success. + * + * @note: + * When an outline, or a sub-path, is `closed', the stroker generates + * two independent `border' outlines, named `left' and `right'. + * + * When the outline, or a sub-path, is `opened', the stroker merges + * the `border' outlines with caps. The `left' border receives all + * points, while the `right' border becomes empty. + * + * Use the function @FT_Stroker_GetCounts instead if you want to + * retrieve the counts associated to both borders. + *} + function FT_Stroker_GetBorderCounts( + stroker: FT_Stroker; + border: FT_StrokerBorder; + out anum_points: FT_UInt; + out anum_contours: FT_UInt ): FT_Error; + cdecl; external ft_lib name 'FT_Stroker_GetBorderCounts'; + + + {************************************************************** + * + * @function: + * FT_Stroker_ExportBorder + * + * @description: + * Call this function after @FT_Stroker_GetBorderCounts to + * export the corresponding border to your own @FT_Outline + * structure. + * + * Note that this function will append the border points and + * contours to your outline, but will not try to resize its + * arrays. + * + * @input: + * stroker :: + * The target stroker handle. + * + * border :: + * The border index. + * + * outline :: + * The target outline handle. + * + * @note: + * Always call this function after @FT_Stroker_GetBorderCounts to + * get sure that there is enough room in your @FT_Outline object to + * receive all new data. + * + * When an outline, or a sub-path, is `closed', the stroker generates + * two independent `border' outlines, named `left' and `right' + * + * When the outline, or a sub-path, is `opened', the stroker merges + * the `border' outlines with caps. The `left' border receives all + * points, while the `right' border becomes empty. + * + * Use the function @FT_Stroker_Export instead if you want to + * retrieve all borders at once. + *} + procedure FT_Stroker_ExportBorder( + stroker: FT_Stroker; + border: FT_StrokerBorder; + outline: PFT_Outline ); + cdecl; external ft_lib name 'FT_Stroker_ExportBorder'; + + + {************************************************************** + * + * @function: + * FT_Stroker_GetCounts + * + * @description: + * Call this function once you have finished parsing your paths + * with the stroker. It returns the number of points and + * contours necessary to export all points/borders from the stroked + * outline/path. + * + * @input: + * stroker :: + * The target stroker handle. + * + * @output: + * anum_points :: + * The number of points. + * + * anum_contours :: + * The number of contours. + * + * @return: + * FreeType error code. 0 means success. + *} + function FT_Stroker_GetCounts( + stroker: FT_Stroker; + out anum_points: FT_UInt; + out anum_contours: FT_UInt ): FT_Error; + cdecl; external ft_lib name 'FT_Stroker_GetCounts'; + + + {************************************************************** + * + * @function: + * FT_Stroker_Export + * + * @description: + * Call this function after @FT_Stroker_GetBorderCounts to + * export the all borders to your own @FT_Outline structure. + * + * Note that this function will append the border points and + * contours to your outline, but will not try to resize its + * arrays. + * + * @input: + * stroker :: + * The target stroker handle. + * + * outline :: + * The target outline handle. + *} + procedure FT_Stroker_Export( + stroker: FT_Stroker; + outline: PFT_Outline ); + cdecl; external ft_lib name 'FT_Stroker_Export'; + + + {************************************************************** + * + * @function: + * FT_Stroker_Done + * + * @description: + * Destroy a stroker object. + * + * @input: + * stroker :: + * A stroker handle. Can be NULL. + *} + procedure FT_Stroker_Done( stroker: FT_Stroker ); + cdecl; external ft_lib name 'FT_Stroker_Done'; + + + {************************************************************** + * + * @function: + * FT_Glyph_Stroke + * + * @description: + * Stroke a given outline glyph object with a given stroker. + * + * @inout: + * pglyph :: + * Source glyph handle on input, new glyph handle on output. + * + * @input: + * stroker :: + * A stroker handle. + * + * destroy :: + * A Boolean. If 1, the source glyph object is destroyed + * on success. + * + * @return: + * FreeType error code. 0 means success. + * + * @note: + * The source glyph is untouched in case of error. + *} + function FT_Glyph_Stroke( + var glyph: FT_Glyph; + stroker: FT_Stroker; + destroy: FT_Bool ): FT_Error; + cdecl; external ft_lib name 'FT_Glyph_Stroke'; + + + {************************************************************** + * + * @function: + * FT_Glyph_StrokeBorder + * + * @description: + * Stroke a given outline glyph object with a given stroker, but + * only return either its inside or outside border. + * + * @inout: + * pglyph :: + * Source glyph handle on input, new glyph handle on output. + * + * @input: + * stroker :: + * A stroker handle. + * + * inside :: + * A Boolean. If 1, return the inside border, otherwise + * the outside border. + * + * destroy :: + * A Boolean. If 1, the source glyph object is destroyed + * on success. + * + * @return: + * FreeType error code. 0 means success. + * + * @note: + * The source glyph is untouched in case of error. + *} + function FT_Glyph_StrokeBorder( + var glyph: FT_Glyph; + stroker: FT_Stroker; + inside: FT_Bool; + destroy: FT_Bool ): FT_Error; + cdecl; external ft_lib name 'FT_Glyph_StrokeBorder'; + +{$ENDIF TYPE_DECL} + diff --git a/Lua/src/lib/freetype/fttypes.inc b/Lua/src/lib/freetype/fttypes.inc new file mode 100644 index 00000000..a64432e6 --- /dev/null +++ b/Lua/src/lib/freetype/fttypes.inc @@ -0,0 +1,311 @@ +(***************************************************************************) +(* *) +(* fttypes.h *) +(* *) +(* FreeType simple types definitions (specification only). *) +(* *) +(* Copyright 1996-2001, 2002, 2004, 2006, 2007 by *) +(* David Turner, Robert Wilhelm, and Werner Lemberg. *) +(* *) +(* This file is part of the FreeType project, and may only be used, *) +(* modified, and distributed under the terms of the FreeType project *) +(* license, LICENSE.TXT. By continuing to use, modify, or distribute *) +(* this file you indicate that you have read the license and *) +(* understand and accept it fully. *) +(* *) +(***************************************************************************) +(***************************************************************************) +(* Pascal port by the UltraStar Deluxe Team *) +(***************************************************************************) + + (*************************************************************************) + (* *) + (* <Section> *) + (* basic_types *) + (* *) + (* <Title> *) + (* Basic Data Types *) + (* *) + (* <Abstract> *) + (* The basic data types defined by the library. *) + (* *) + (* <Description> *) + (* This section contains the basic data types defined by FreeType 2, *) + (* ranging from simple scalar types to bitmap descriptors. More *) + (* font-specific structures are defined in a different section. *) + (* *) + (* <Order> *) + (* FT_Byte *) + (* FT_Bytes *) + (* FT_Char *) + (* FT_Int *) + (* FT_UInt *) + (* FT_Short *) + (* FT_UShort *) + (* FT_Long *) + (* FT_ULong *) + (* FT_Bool *) + (* FT_Offset *) + (* FT_PtrDist *) + (* FT_String *) + (* FT_Tag *) + (* FT_Error *) + (* FT_Fixed *) + (* FT_Pointer *) + (* FT_Pos *) + (* FT_Vector *) + (* FT_BBox *) + (* FT_Matrix *) + (* FT_FWord *) + (* FT_UFWord *) + (* FT_F2Dot14 *) + (* FT_UnitVector *) + (* FT_F26Dot6 *) + (* *) + (* *) + (* FT_Generic *) + (* FT_Generic_Finalizer *) + (* *) + (* FT_Bitmap *) + (* FT_Pixel_Mode *) + (* FT_Palette_Mode *) + (* FT_Glyph_Format *) + (* FT_IMAGE_TAG *) + (* *) + (*************************************************************************) + +{$IFDEF TYPE_DECL} + + (*************************************************************************) + (* *) + (* <Type> *) + (* FT_Bool *) + (* *) + (* <Description> *) + (* A typedef of unsigned char, used for simple booleans. As usual, *) + (* values 1 and 0 represent true and false, respectively. *) + (* *) + FT_Bool = cuchar; +{$ENDIF TYPE_DECL} +{$IFNDEF TYPE_DECL} +const + FT_FALSE = 0; + FT_TRUE = 1; +{$ENDIF !TYPE_DECL} +{$IFDEF TYPE_DECL} + + (*************************************************************************) + (* *) + (* <Type> *) + (* FT_Byte *) + (* *) + (* <Description> *) + (* A simple typedef for the _unsigned_ char type. *) + (* *) + FT_Byte = cuchar; + PFT_Byte = ^FT_Byte; + + + (*************************************************************************) + (* *) + (* <Type> *) + (* FT_String *) + (* *) + (* <Description> *) + (* A simple typedef for the char type, usually used for strings. *) + (* *) + FT_String = cchar; + PFT_String = ^FT_String; + + + (*************************************************************************) + (* *) + (* <Type> *) + (* FT_Short *) + (* *) + (* <Description> *) + (* A typedef for signed short. *) + (* *) + FT_Short = csshort; + PFT_Short = ^FT_Short; + + PFT_ShortArray = ^FT_ShortArray; + FT_ShortArray = array[0 .. (MaxInt div SizeOf(FT_Short))-1] of FT_Short; + + (*************************************************************************) + (* *) + (* <Type> *) + (* FT_UShort *) + (* *) + (* <Description> *) + (* A typedef for unsigned short. *) + (* *) + FT_UShort = cushort; + + + (*************************************************************************) + (* *) + (* <Type> *) + (* FT_Int *) + (* *) + (* <Description> *) + (* A typedef for the int type. *) + (* *) + FT_Int = csint; + + + (*************************************************************************) + (* *) + (* <Type> *) + (* FT_UInt *) + (* *) + (* <Description> *) + (* A typedef for the unsigned int type. *) + (* *) + FT_UInt = cuint; + + + (*************************************************************************) + (* *) + (* <Type> *) + (* FT_Long *) + (* *) + (* <Description> *) + (* A typedef for signed long. *) + (* *) + FT_Long = cslong; + + + (*************************************************************************) + (* *) + (* <Type> *) + (* FT_ULong *) + (* *) + (* <Description> *) + (* A typedef for unsigned long. *) + (* *) + FT_ULong = culong; + + + (*************************************************************************) + (* *) + (* <Type> *) + (* FT_F26Dot6 *) + (* *) + (* <Description> *) + (* A signed 26.6 fixed float type used for vectorial pixel *) + (* coordinates. *) + (* *) + FT_F26Dot6 = cslong; + + + (*************************************************************************) + (* *) + (* <Type> *) + (* FT_Fixed *) + (* *) + (* <Description> *) + (* This type is used to store 16.16 fixed float values, like scaling *) + (* values or matrix coefficients. *) + (* *) + FT_Fixed = cslong; + + + (*************************************************************************) + (* *) + (* <Type> *) + (* FT_Error *) + (* *) + (* <Description> *) + (* The FreeType error code type. A value of 0 is always interpreted *) + (* as a successful operation. *) + (* *) + FT_Error = cint; + + + (*************************************************************************) + (* *) + (* <Struct> *) + (* FT_Matrix *) + (* *) + (* <Description> *) + (* A simple structure used to store a 2x2 matrix. Coefficients are *) + (* in 16.16 fixed float format. The computation performed is: *) + (* *) + (* { *) + (* x' = x*xx + y*xy *) + (* y' = x*yx + y*yy *) + (* } *) + (* *) + (* <Fields> *) + (* xx :: Matrix coefficient. *) + (* *) + (* xy :: Matrix coefficient. *) + (* *) + (* yx :: Matrix coefficient. *) + (* *) + (* yy :: Matrix coefficient. *) + (* *) + PFT_Matrix = ^FT_Matrix; + FT_Matrix = record + xx, xy: FT_Fixed; + yx, yy: FT_Fixed; + end; + + + (*************************************************************************) + (* *) + (* <FuncType> *) + (* FT_Generic_Finalizer *) + (* *) + (* <Description> *) + (* Describes a function used to destroy the `client' data of any *) + (* FreeType object. See the description of the FT_Generic type for *) + (* details of usage. *) + (* *) + (* <Input> *) + (* The address of the FreeType object which is under finalization. *) + (* Its client data is accessed through its `generic' field. *) + (* *) + FT_Generic_Finalizer = procedure(AnObject : pointer ); cdecl; + + + (*************************************************************************) + (* *) + (* <Struct> *) + (* FT_Generic *) + (* *) + (* <Description> *) + (* Client applications often need to associate their own data to a *) + (* variety of FreeType core objects. For example, a text layout API *) + (* might want to associate a glyph cache to a given size object. *) + (* *) + (* Most FreeType object contains a `generic' field, of type *) + (* FT_Generic, which usage is left to client applications and font *) + (* servers. *) + (* *) + (* It can be used to store a pointer to client-specific data, as well *) + (* as the address of a `finalizer' function, which will be called by *) + (* FreeType when the object is destroyed (for example, the previous *) + (* client example would put the address of the glyph cache destructor *) + (* in the `finalizer' field). *) + (* *) + (* <Fields> *) + (* data :: A typeless pointer to any client-specified data. This *) + (* field is completely ignored by the FreeType library. *) + (* *) + (* finalizer :: A pointer to a `generic finalizer' function, which *) + (* will be called when the object is destroyed. If this *) + (* field is set to NULL, no code will be called. *) + (* *) + FT_Generic = record + data: pointer; + finalizer: FT_Generic_Finalizer; + end; + + + TByteArray = array [0 .. (MaxInt div SizeOf(byte))-1] of byte; + PByteArray = ^TByteArray; + +{$ENDIF TYPE_DECL} + diff --git a/Lua/src/lib/lib-info.txt b/Lua/src/lib/lib-info.txt index 59502c7a..0a184568 100644 --- a/Lua/src/lib/lib-info.txt +++ b/Lua/src/lib/lib-info.txt @@ -1,60 +1,60 @@ -bass:
-http://www.un4seen.com/ (2.4.2.1)
-- FPC Mac OS X compatibility fixes
-
-fft:
-translation of audacity's FFT.cpp by hennymcc (maybe replace this with FFTW?)
-
-ffmpeg:
-- http://www.iversenit.dk/dev/ffmpeg-headers/: 2006-10
-- several bugs were fixed
-- many IFDEFS were added to the header to support multiple versions of ffmpeg (starting with end of 2006) and not only one specific version. This is necessary as we cannot control which version is used on linux. We could ship the ffmpeg lib with USDX and link statically but a stripped down ffmpeg is 15MB in size and takes 5 minutes to compile (so static linkage is not a good option).
-- the headers were updated to reflect the changes in the ffmpeg C-headers (http://svn.mplayerhq.hu/ffmpeg/trunk/ and http://svn.mplayerhq.hu/mplayer/trunk/libswscale/)
-
-freeimage:
-- inserted by eddie. Some compatibility fixes for platforms different than mac os x.
-- not used anymore
-
-freetype:
-- based on the AggPas (http://aggpas.org/) headers
-- just a minimal header that contains only some of the freetype functions and types. Some functions and structures/constants/types needed for USDX were added.
-- some comments added
-
-jedi-sdl:
-JEDI-SDL v1.0 Final RC 2 (http://jedi-sdl.pascalgamedevelopment.com/)
-- 64bit compatibility patch (http://sourceforge.net/tracker/index.php?func=detail&aid=1902924&group_id=43805&atid=437446)
-- some Mac OS X patches from freepascal trunk
-- some additional patched (see *.patch)
-
-midi:
-taken from http://www.torry.net/authorsmore.php?id=1615 (TMidiPlayer)
-- FPC (Win32) compatibility fixes
-- Win32 only. Maybe use some timidity stuff under linux.
-
-libpng:
-autocreated H2Pas file taken from freepascal trunk
-- bug fixes (especially H2Pas related stuff like wrong file types)
-- delphi compatibility
-- comments added
-
-portaudio:
-translation of the (patched) audacity C headers by hennymcc.
-See http://audacity.cvs.sourceforge.net/viewvc/audacity/lib-src/portaudio-v19/include/?sortdir=down
-
-portmixer:
-translation of the (patched) audacity C headers by hennymcc.
-- Unlike portaudio portmixer is part of audacity and there is no linux package for it. If we want to use it for linux, we have to link it statically. Unfortunately it requires a patched version of portaudio (which is part of audacity and statically linked to) so we have to statically link portaudio too :(.
-
-projectM:
-translation of the original C++ headers and C-wrapper by hennymcc
-
-samplerate:
-translation of the original C headers by profoX/hennymcc
-
-sqlite:
-taken from http://www.itwriting.com/blog/a-simple-delphi-wrapper-for-sqlite-3
-- slightly patched: see *.patch files for what has been patched (e.g. Binding)
-
-zlib:
-taken from freepascal (slightly patched)
+bass: +http://www.un4seen.com/ (2.4.2.1) +- FPC Mac OS X compatibility fixes + +fft: +translation of audacity's FFT.cpp by hennymcc (maybe replace this with FFTW?) + +ffmpeg: +- http://www.iversenit.dk/dev/ffmpeg-headers/: 2006-10 +- several bugs were fixed +- many IFDEFS were added to the header to support multiple versions of ffmpeg (starting with end of 2006) and not only one specific version. This is necessary as we cannot control which version is used on linux. We could ship the ffmpeg lib with USDX and link statically but a stripped down ffmpeg is 15MB in size and takes 5 minutes to compile (so static linkage is not a good option). +- the headers were updated to reflect the changes in the ffmpeg C-headers (http://svn.mplayerhq.hu/ffmpeg/trunk/ and http://svn.mplayerhq.hu/mplayer/trunk/libswscale/) + +freeimage: +- inserted by eddie. Some compatibility fixes for platforms different than mac os x. +- not used anymore + +freetype: +- based on the AggPas (http://aggpas.org/) headers +- just a minimal header that contains only some of the freetype functions and types. Some functions and structures/constants/types needed for USDX were added. +- some comments added + +jedi-sdl: +JEDI-SDL v1.0 Final RC 2 (http://jedi-sdl.pascalgamedevelopment.com/) +- 64bit compatibility patch (http://sourceforge.net/tracker/index.php?func=detail&aid=1902924&group_id=43805&atid=437446) +- some Mac OS X patches from freepascal trunk +- some additional patched (see *.patch) + +midi: +taken from http://www.torry.net/authorsmore.php?id=1615 (TMidiPlayer) +- FPC (Win32) compatibility fixes +- Win32 only. Maybe use some timidity stuff under linux. + +libpng: +autocreated H2Pas file taken from freepascal trunk +- bug fixes (especially H2Pas related stuff like wrong file types) +- delphi compatibility +- comments added + +portaudio: +translation of the (patched) audacity C headers by hennymcc. +See http://audacity.cvs.sourceforge.net/viewvc/audacity/lib-src/portaudio-v19/include/?sortdir=down + +portmixer: +translation of the (patched) audacity C headers by hennymcc. +- Unlike portaudio portmixer is part of audacity and there is no linux package for it. If we want to use it for linux, we have to link it statically. Unfortunately it requires a patched version of portaudio (which is part of audacity and statically linked to) so we have to statically link portaudio too :(. + +projectM: +translation of the original C++ headers and C-wrapper by hennymcc + +samplerate: +translation of the original C headers by profoX/hennymcc + +sqlite: +taken from http://www.itwriting.com/blog/a-simple-delphi-wrapper-for-sqlite-3 +- slightly patched: see *.patch files for what has been patched (e.g. Binding) + +zlib: +taken from freepascal (slightly patched) - delphi compatibility
\ No newline at end of file diff --git a/Lua/src/lib/midi/CIRCBUF.PAS b/Lua/src/lib/midi/CIRCBUF.PAS index 77cb3643..3ceb4c6e 100644 --- a/Lua/src/lib/midi/CIRCBUF.PAS +++ b/Lua/src/lib/midi/CIRCBUF.PAS @@ -23,7 +23,7 @@ interface {$IFDEF FPC} {$MODE Delphi} - {$H+} // use AnsiString + {$H+} // use long strings {$ENDIF} Uses diff --git a/Lua/src/lib/midi/DELPHMCB.PAS b/Lua/src/lib/midi/DELPHMCB.PAS index e607627d..ef0d5451 100644 --- a/Lua/src/lib/midi/DELPHMCB.PAS +++ b/Lua/src/lib/midi/DELPHMCB.PAS @@ -13,7 +13,7 @@ interface {$IFDEF FPC} {$MODE Delphi} - {$H+} // use AnsiString + {$H+} // use long strings {$ENDIF} uses diff --git a/Lua/src/lib/midi/MIDIDEFS.PAS b/Lua/src/lib/midi/MIDIDEFS.PAS index fc8eed26..4afe56ef 100644 --- a/Lua/src/lib/midi/MIDIDEFS.PAS +++ b/Lua/src/lib/midi/MIDIDEFS.PAS @@ -13,7 +13,7 @@ interface {$IFDEF FPC} {$MODE Delphi} - {$H+} // use AnsiString + {$H+} // use long strings {$ENDIF} uses diff --git a/Lua/src/lib/midi/MIDITYPE.PAS b/Lua/src/lib/midi/MIDITYPE.PAS index b1ec1bdd..45b50820 100644 --- a/Lua/src/lib/midi/MIDITYPE.PAS +++ b/Lua/src/lib/midi/MIDITYPE.PAS @@ -10,7 +10,7 @@ interface {$IFDEF FPC} {$MODE Delphi} - {$H+} // use AnsiString + {$H+} // use long strings {$ENDIF} uses diff --git a/Lua/src/lib/midi/MidiFile.pas b/Lua/src/lib/midi/MidiFile.pas index 11b1ca0b..acf44c04 100644 --- a/Lua/src/lib/midi/MidiFile.pas +++ b/Lua/src/lib/midi/MidiFile.pas @@ -92,18 +92,18 @@ interface {$IFDEF FPC} {$MODE Delphi} - {$H+} // use AnsiString + {$H+} // use long strings {$ENDIF} uses Windows, - //Forms, Messages, Classes, {$IFDEF FPC} WinAllocation, {$ENDIF} - SysUtils; + SysUtils, + UPath; type TChunkType = (illegal, header, track); @@ -162,7 +162,7 @@ type procedure WndProc(var Msg : TMessage); protected { Protected declarations } - midiFile: file of byte; + midiFile: TBinaryFileStream; chunkType: TChunkType; chunkLength: integer; chunkData: PByte; @@ -177,7 +177,7 @@ type FBpm: integer; FBeatsPerMeasure: integer; FusPerTick: double; - FFilename: string; + FFilename: IPath; Tracks: TList; currentTrack: TMidiTrack; @@ -191,7 +191,7 @@ type currentPos: Double; // Current Position in ticks procedure OnTrackReady; - procedure setFilename(val: string); + procedure SetFilename(val: IPath); procedure ReadChunkHeader; procedure ReadChunkContent; procedure ReadChunk; @@ -221,7 +221,7 @@ type function Ready: boolean; published { Published declarations } - property Filename: string read FFilename write setFilename; + property Filename: IPath read FFilename write SetFilename; property NumberOfTracks: integer read numberTracks; property TicksPerQuarter: integer read deltaTicks; property FileFormat: TFileFormat read FFileFormat; @@ -463,7 +463,7 @@ begin result := Tracks.Items[index]; end; -procedure TMidifile.setFilename(val: string); +procedure TMidifile.SetFilename(val: IPath); begin FFilename := val; // ReadFile; @@ -586,7 +586,7 @@ procedure TMidifile.ReadChunkHeader; var theByte: array[0..7] of byte; begin - BlockRead(midiFile, theByte, 8); + midiFile.Read(theByte[0], 8); if (theByte[0] = $4D) and (theByte[1] = $54) then begin if (theByte[2] = $68) and (theByte[3] = $64) then @@ -608,7 +608,7 @@ begin if not (chunkData = nil) then FreeMem(chunkData); GetMem(chunkData, chunkLength + 10); - BlockRead(midiFile, chunkData^, chunkLength); + midiFile.Read(chunkData^, chunkLength); chunkIndex := chunkData; chunkEnd := PByte(integer(chunkIndex) + integer(chunkLength) - 1); end; @@ -848,12 +848,10 @@ begin Tracks.Clear; chunkType := illegal; - AssignFile(midiFile, FFilename); - FileMode := 0; - Reset(midiFile); - while not eof(midiFile) do + midiFile := TBinaryFileStream.Create(FFilename, fmOpenRead); + while (midiFile.Position < midiFile.Size) do ReadChunk; - CloseFile(midiFile); + FreeAndNil(midiFile); numberTracks := Tracks.Count; end; diff --git a/Lua/src/lib/midi/MidiScope.pas b/Lua/src/lib/midi/MidiScope.pas index 42fc65fc..afc20b0f 100644 --- a/Lua/src/lib/midi/MidiScope.pas +++ b/Lua/src/lib/midi/MidiScope.pas @@ -20,7 +20,7 @@ interface {$IFDEF FPC} {$MODE Delphi} - {$H+} // use AnsiString + {$H+} // use long strings {$ENDIF} uses diff --git a/Lua/src/lib/midi/Midicons.pas b/Lua/src/lib/midi/Midicons.pas index 35dbb5f3..72259beb 100644 --- a/Lua/src/lib/midi/Midicons.pas +++ b/Lua/src/lib/midi/Midicons.pas @@ -11,7 +11,7 @@ interface {$IFDEF FPC} {$MODE Delphi} - {$H+} // use AnsiString + {$H+} // use long strings {$ENDIF} uses Messages; diff --git a/Lua/src/lib/midi/Midiin.pas b/Lua/src/lib/midi/Midiin.pas index 21db0298..66e4f76d 100644 --- a/Lua/src/lib/midi/Midiin.pas +++ b/Lua/src/lib/midi/Midiin.pas @@ -103,7 +103,7 @@ interface {$IFDEF FPC} {$MODE Delphi} - {$H+} // use AnsiString + {$H+} // use long strings {$ENDIF} uses diff --git a/Lua/src/lib/midi/Midiout.pas b/Lua/src/lib/midi/Midiout.pas index 606d0dae..98e6e3fb 100644 --- a/Lua/src/lib/midi/Midiout.pas +++ b/Lua/src/lib/midi/Midiout.pas @@ -98,7 +98,7 @@ interface {$IFDEF FPC} {$MODE Delphi} - {$H+} // use AnsiString + {$H+} // use long strings {$ENDIF} uses diff --git a/Lua/src/lib/other/DirWatch.pas b/Lua/src/lib/other/DirWatch.pas index 9d395840..1e00ec5d 100644 --- a/Lua/src/lib/other/DirWatch.pas +++ b/Lua/src/lib/other/DirWatch.pas @@ -25,7 +25,7 @@ interface {$IFDEF FPC} {$MODE Delphi} - {$H+} // use AnsiString + {$H+} // use long strings {$ENDIF} uses diff --git a/Lua/src/lib/pcre/pcre.pas b/Lua/src/lib/pcre/pcre.pas new file mode 100644 index 00000000..50e3371a --- /dev/null +++ b/Lua/src/lib/pcre/pcre.pas @@ -0,0 +1,852 @@ +{**************************************************************************************************} +{ } +{ Project JEDI Code Library (JCL) } +{ } +{ The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); } +{ you may not use this file except in compliance with the License. You may obtain a copy of the } +{ License at http://www.mozilla.org/MPL/ } +{ } +{ Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF } +{ ANY KIND, either express or implied. See the License for the specific language governing rights } +{ and limitations under the License. } +{ } +{ The Original Code is JclPRCE.pas. } +{ } +{ The Initial Developer of the Original Code is Peter Thornqvist. } +{ Portions created by Peter Thornqvist are Copyright (C) of Peter Thornqvist. All rights reserved. } +{ Portions created by University of Cambridge are } +{ Copyright (C) 1997-2001 by University of Cambridge. } +{ } +{ Contributor(s): } +{ Robert Rossmair (rrossmair) } +{ Mario R. Carro } +{ Florent Ouchet (outchy) } +{ } +{ The latest release of PCRE is always available from } +{ ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/pcre-xxx.tar.gz } +{ } +{**************************************************************************************************} +{ } +{ Header conversion of pcre.h } +{ } +{ } +{**************************************************************************************************} +{ } +{ Last modified: $Date:: $ } +{ Revision: $Rev:: $ } +{ Author: $Author:: $ } +{ } +{**************************************************************************************************} + +unit pcre; + +interface + +(************************************************* +* Perl-Compatible Regular Expressions * +*************************************************) + +{$IFDEF FPC} + {$MODE DELPHI} + {$PACKENUM 4} (* use 4-byte enums *) + {$PACKRECORDS C} (* C/C++-compatible record packing *) +{$ELSE} + {$MINENUMSIZE 4} (* use 4-byte enums *) +{$ENDIF} + +{$WEAKPACKAGEUNIT ON} + +(*$HPPEMIT '#include "pcre.h"'*) + +const + MAX_PATTERN_LENGTH = $10003; + {$EXTERNALSYM MAX_PATTERN_LENGTH} + MAX_QUANTIFY_REPEAT = $10000; + {$EXTERNALSYM MAX_QUANTIFY_REPEAT} + MAX_CAPTURE_COUNT = $FFFF; + {$EXTERNALSYM MAX_CAPTURE_COUNT} + MAX_NESTING_DEPTH = 200; + {$EXTERNALSYM MAX_NESTING_DEPTH} + +const + (* Options *) + PCRE_CASELESS = $00000001; + {$EXTERNALSYM PCRE_CASELESS} + PCRE_MULTILINE = $00000002; + {$EXTERNALSYM PCRE_MULTILINE} + PCRE_DOTALL = $00000004; + {$EXTERNALSYM PCRE_DOTALL} + PCRE_EXTENDED = $00000008; + {$EXTERNALSYM PCRE_EXTENDED} + PCRE_ANCHORED = $00000010; + {$EXTERNALSYM PCRE_ANCHORED} + PCRE_DOLLAR_ENDONLY = $00000020; + {$EXTERNALSYM PCRE_DOLLAR_ENDONLY} + PCRE_EXTRA = $00000040; + {$EXTERNALSYM PCRE_EXTRA} + PCRE_NOTBOL = $00000080; + {$EXTERNALSYM PCRE_NOTBOL} + PCRE_NOTEOL = $00000100; + {$EXTERNALSYM PCRE_NOTEOL} + PCRE_UNGREEDY = $00000200; + {$EXTERNALSYM PCRE_UNGREEDY} + PCRE_NOTEMPTY = $00000400; + {$EXTERNALSYM PCRE_NOTEMPTY} + PCRE_UTF8 = $00000800; + {$EXTERNALSYM PCRE_UTF8} + PCRE_NO_AUTO_CAPTURE = $00001000; + {$EXTERNALSYM PCRE_NO_AUTO_CAPTURE} + PCRE_NO_UTF8_CHECK = $00002000; + {$EXTERNALSYM PCRE_NO_UTF8_CHECK} + PCRE_AUTO_CALLOUT = $00004000; + {$EXTERNALSYM PCRE_AUTO_CALLOUT} + PCRE_PARTIAL_SOFT = $00008000; + {$EXTERNALSYM PCRE_PARTIAL_SOFT} + PCRE_PARTIAL = PCRE_PARTIAL_SOFT; // Backwards compatible synonym + {$EXTERNALSYM PCRE_PARTIAL} + PCRE_DFA_SHORTEST = $00010000; + {$EXTERNALSYM PCRE_DFA_SHORTEST} + PCRE_DFA_RESTART = $00020000; + {$EXTERNALSYM PCRE_DFA_RESTART} + PCRE_FIRSTLINE = $00040000; + {$EXTERNALSYM PCRE_FIRSTLINE} + PCRE_DUPNAMES = $00080000; + {$EXTERNALSYM PCRE_DUPNAMES} + PCRE_NEWLINE_CR = $00100000; + {$EXTERNALSYM PCRE_NEWLINE_CR} + PCRE_NEWLINE_LF = $00200000; + {$EXTERNALSYM PCRE_NEWLINE_LF} + PCRE_NEWLINE_CRLF = $00300000; + {$EXTERNALSYM PCRE_NEWLINE_CRLF} + PCRE_NEWLINE_ANY = $00400000; + {$EXTERNALSYM PCRE_NEWLINE_ANY} + PCRE_NEWLINE_ANYCRLF = $00500000; + {$EXTERNALSYM PCRE_NEWLINE_ANYCRLF} + PCRE_BSR_ANYCRLF = $00800000; + {$EXTERNALSYM PCRE_BSR_ANYCRLF} + PCRE_BSR_UNICODE = $01000000; + {$EXTERNALSYM PCRE_BSR_UNICODE} + PCRE_JAVASCRIPT_COMPAT = $02000000; + {$EXTERNALSYM PCRE_JAVASCRIPT_COMPAT} + PCRE_NO_START_OPTIMIZE = $04000000; + {$EXTERNALSYM PCRE_NO_START_OPTIMIZE} + PCRE_NO_START_OPTIMISE = $04000000; + {$EXTERNALSYM PCRE_NO_START_OPTIMISE} + PCRE_PARTIAL_HARD = $08000000; + {$EXTERNALSYM PCRE_PARTIAL_HARD} + PCRE_NOTEMPTY_ATSTART = $10000000; + {$EXTERNALSYM PCRE_NOTEMPTY_ATSTART} + + (* Exec-time and get-time error codes *) + + PCRE_ERROR_NOMATCH = -1; + {$EXTERNALSYM PCRE_ERROR_NOMATCH} + PCRE_ERROR_NULL = -2; + {$EXTERNALSYM PCRE_ERROR_NULL} + PCRE_ERROR_BADOPTION = -3; + {$EXTERNALSYM PCRE_ERROR_BADOPTION} + PCRE_ERROR_BADMAGIC = -4; + {$EXTERNALSYM PCRE_ERROR_BADMAGIC} + PCRE_ERROR_UNKNOWN_NODE = -5; + {$EXTERNALSYM PCRE_ERROR_UNKNOWN_NODE} + PCRE_ERROR_NOMEMORY = -6; + {$EXTERNALSYM PCRE_ERROR_NOMEMORY} + PCRE_ERROR_NOSUBSTRING = -7; + {$EXTERNALSYM PCRE_ERROR_NOSUBSTRING} + PCRE_ERROR_MATCHLIMIT = -8; + {$EXTERNALSYM PCRE_ERROR_MATCHLIMIT} + PCRE_ERROR_CALLOUT = -9; (* Never used by PCRE itself *) + {$EXTERNALSYM PCRE_ERROR_CALLOUT} + PCRE_ERROR_BADUTF8 = -10; + {$EXTERNALSYM PCRE_ERROR_BADUTF8} + PCRE_ERROR_BADUTF8_OFFSET = -11; + {$EXTERNALSYM PCRE_ERROR_BADUTF8_OFFSET} + PCRE_ERROR_PARTIAL = -12; + {$EXTERNALSYM PCRE_ERROR_PARTIAL} + PCRE_ERROR_BADPARTIAL = -13; + {$EXTERNALSYM PCRE_ERROR_BADPARTIAL} + PCRE_ERROR_INTERNAL = -14; + {$EXTERNALSYM PCRE_ERROR_INTERNAL} + PCRE_ERROR_BADCOUNT = -15; + {$EXTERNALSYM PCRE_ERROR_BADCOUNT} + PCRE_ERROR_DFA_UITEM = -16; + {$EXTERNALSYM PCRE_ERROR_DFA_UITEM} + PCRE_ERROR_DFA_UCOND = -17; + {$EXTERNALSYM PCRE_ERROR_DFA_UCOND} + PCRE_ERROR_DFA_UMLIMIT = -18; + {$EXTERNALSYM PCRE_ERROR_DFA_UMLIMIT} + PCRE_ERROR_DFA_WSSIZE = -19; + {$EXTERNALSYM PCRE_ERROR_DFA_WSSIZE} + PCRE_ERROR_DFA_RECURSE = -20; + {$EXTERNALSYM PCRE_ERROR_DFA_RECURSE} + PCRE_ERROR_RECURSIONLIMIT = -21; + {$EXTERNALSYM PCRE_ERROR_RECURSIONLIMIT} + PCRE_ERROR_NULLWSLIMIT = -22; (* No longer actually used *) + {$EXTERNALSYM PCRE_ERROR_NULLWSLIMIT} + PCRE_ERROR_BADNEWLINE = -23; + {$EXTERNALSYM PCRE_ERROR_BADNEWLINE} + + (* Request types for pcre_fullinfo() *) + + PCRE_INFO_OPTIONS = 0; + {$EXTERNALSYM PCRE_INFO_OPTIONS} + PCRE_INFO_SIZE = 1; + {$EXTERNALSYM PCRE_INFO_SIZE} + PCRE_INFO_CAPTURECOUNT = 2; + {$EXTERNALSYM PCRE_INFO_CAPTURECOUNT} + PCRE_INFO_BACKREFMAX = 3; + {$EXTERNALSYM PCRE_INFO_BACKREFMAX} + PCRE_INFO_FIRSTCHAR = 4; + {$EXTERNALSYM PCRE_INFO_FIRSTCHAR} + PCRE_INFO_FIRSTTABLE = 5; + {$EXTERNALSYM PCRE_INFO_FIRSTTABLE} + PCRE_INFO_LASTLITERAL = 6; + {$EXTERNALSYM PCRE_INFO_LASTLITERAL} + PCRE_INFO_NAMEENTRYSIZE = 7; + {$EXTERNALSYM PCRE_INFO_NAMEENTRYSIZE} + PCRE_INFO_NAMECOUNT = 8; + {$EXTERNALSYM PCRE_INFO_NAMECOUNT} + PCRE_INFO_NAMETABLE = 9; + {$EXTERNALSYM PCRE_INFO_NAMETABLE} + PCRE_INFO_STUDYSIZE = 10; + {$EXTERNALSYM PCRE_INFO_STUDYSIZE} + PCRE_INFO_DEFAULT_TABLES = 11; + {$EXTERNALSYM PCRE_INFO_DEFAULT_TABLES} + PCRE_INFO_OKPARTIAL = 12; + {$EXTERNALSYM PCRE_INFO_OKPARTIAL} + PCRE_INFO_JCHANGED = 13; + {$EXTERNALSYM PCRE_INFO_JCHANGED} + PCRE_INFO_HASCRORLF = 14; + {$EXTERNALSYM PCRE_INFO_HASCRORLF} + PCRE_INFO_MINLENGTH = 15; + {$EXTERNALSYM PCRE_INFO_MINLENGTH} + + (* Request types for pcre_config() *) + PCRE_CONFIG_UTF8 = 0; + {$EXTERNALSYM PCRE_CONFIG_UTF8} + PCRE_CONFIG_NEWLINE = 1; + {$EXTERNALSYM PCRE_CONFIG_NEWLINE} + PCRE_CONFIG_LINK_SIZE = 2; + {$EXTERNALSYM PCRE_CONFIG_LINK_SIZE} + PCRE_CONFIG_POSIX_MALLOC_THRESHOLD = 3; + {$EXTERNALSYM PCRE_CONFIG_POSIX_MALLOC_THRESHOLD} + PCRE_CONFIG_MATCH_LIMIT = 4; + {$EXTERNALSYM PCRE_CONFIG_MATCH_LIMIT} + PCRE_CONFIG_STACKRECURSE = 5; + {$EXTERNALSYM PCRE_CONFIG_STACKRECURSE} + PCRE_CONFIG_UNICODE_PROPERTIES = 6; + {$EXTERNALSYM PCRE_CONFIG_UNICODE_PROPERTIES} + PCRE_CONFIG_MATCH_LIMIT_RECURSION = 7; + {$EXTERNALSYM PCRE_CONFIG_MATCH_LIMIT_RECURSION} + PCRE_CONFIG_BSR = 8; + {$EXTERNALSYM PCRE_CONFIG_BSR} + + (* Bit flags for the pcre_extra structure *) + + PCRE_EXTRA_STUDY_DATA = $0001; + {$EXTERNALSYM PCRE_EXTRA_STUDY_DATA} + PCRE_EXTRA_MATCH_LIMIT = $0002; + {$EXTERNALSYM PCRE_EXTRA_MATCH_LIMIT} + PCRE_EXTRA_CALLOUT_DATA = $0004; + {$EXTERNALSYM PCRE_EXTRA_CALLOUT_DATA} + PCRE_EXTRA_TABLES = $0008; + {$EXTERNALSYM PCRE_EXTRA_TABLES} + PCRE_EXTRA_MATCH_LIMIT_RECURSION = $0010; + {$EXTERNALSYM PCRE_EXTRA_MATCH_LIMIT_RECURSION} + +type + {$IFNDEF FPC} + {$IFDEF CPU64} + SizeInt = Int64; + {$ELSE ~CPU64} + SizeInt = Integer; + {$ENDIF ~CPU64} + PPAnsiChar = ^PAnsiChar; + {$ENDIF ~FPC} + PPPAnsiChar = ^PPAnsiChar; + + real_pcre = packed record + {magic_number: Longword; + size: Integer; + tables: PAnsiChar; + options: Longword; + top_bracket: Word; + top_backref: word; + first_char: PAnsiChar; + req_char: PAnsiChar; + code: array [0..0] of AnsiChar;} + end; + TPCRE = real_pcre; + PPCRE = ^TPCRE; + + real_pcre_extra = packed record + {options: PAnsiChar; + start_bits: array [0..31] of AnsiChar;} + flags: Cardinal; (* Bits for which fields are set *) + study_data: Pointer; (* Opaque data from pcre_study() *) + match_limit: Cardinal; (* Maximum number of calls to match() *) + callout_data: Pointer; (* Data passed back in callouts *) + tables: PAnsiChar; (* Pointer to character tables *) + match_limit_recursion: Cardinal; (* Max recursive calls to match() *) + end; + TPCREExtra = real_pcre_extra; + PPCREExtra = ^TPCREExtra; + + pcre_callout_block = packed record + version: Integer; (* Identifies version of block *) + (* ------------------------ Version 0 ------------------------------- *) + callout_number: Integer; (* Number compiled into pattern *) + offset_vector: PInteger; (* The offset vector *) + subject: PAnsiChar; (* The subject being matched *) + subject_length: Integer; (* The length of the subject *) + start_match: Integer; (* Offset to start of this match attempt *) + current_position: Integer; (* Where we currently are in the subject *) + capture_top: Integer; (* Max current capture *) + capture_last: Integer; (* Most recently closed capture *) + callout_data: Pointer; (* Data passed in with the call *) + (* ------------------- Added for Version 1 -------------------------- *) + pattern_position: Integer; (* Offset to next item in the pattern *) + next_item_length: Integer; (* Length of next item in the pattern *) + (* ------------------------------------------------------------------ *) + end; + + pcre_malloc_callback = function(Size: SizeInt): Pointer; cdecl; + {$EXTERNALSYM pcre_malloc_callback} + pcre_free_callback = procedure(P: Pointer); cdecl; + {$EXTERNALSYM pcre_free_callback} + pcre_stack_malloc_callback = function(Size: SizeInt): Pointer; cdecl; + {$EXTERNALSYM pcre_stack_malloc_callback} + pcre_stack_free_callback = procedure(P: Pointer); cdecl; + {$EXTERNALSYM pcre_stack_free_callback} + pcre_callout_callback = function(var callout_block: pcre_callout_block): Integer; cdecl; + {$EXTERNALSYM pcre_callout_callback} + +var + // renamed from "pcre_X" to "pcre_X_func" to allow functions with name "pcre_X" to be + // declared in implementation when static linked + pcre_malloc_func: ^pcre_malloc_callback = nil; + {$EXTERNALSYM pcre_malloc_func} + pcre_free_func: ^pcre_free_callback = nil; + {$EXTERNALSYM pcre_free_func} + pcre_stack_malloc_func: ^pcre_stack_malloc_callback = nil; + {$EXTERNALSYM pcre_stack_malloc_func} + pcre_stack_free_func: ^pcre_stack_free_callback = nil; + {$EXTERNALSYM pcre_stack_free_func} + pcre_callout_func: ^pcre_callout_callback = nil; + {$EXTERNALSYM pcre_callout_func} + +procedure SetPCREMallocCallback(const Value: pcre_malloc_callback); +{$EXTERNALSYM SetPCREMallocCallback} +function GetPCREMallocCallback: pcre_malloc_callback; +{$EXTERNALSYM GetPCREMallocCallback} +function CallPCREMalloc(Size: SizeInt): Pointer; +{$EXTERNALSYM CallPCREMalloc} + +procedure SetPCREFreeCallback(const Value: pcre_free_callback); +{$EXTERNALSYM SetPCREFreeCallback} +function GetPCREFreeCallback: pcre_free_callback; +{$EXTERNALSYM GetPCREFreeCallback} +procedure CallPCREFree(P: Pointer); +{$EXTERNALSYM CallPCREFree} + +procedure SetPCREStackMallocCallback(const Value: pcre_stack_malloc_callback); +{$EXTERNALSYM SetPCREStackMallocCallback} +function GetPCREStackMallocCallback: pcre_stack_malloc_callback; +{$EXTERNALSYM GetPCREStackMallocCallback} +function CallPCREStackMalloc(Size: SizeInt): Pointer; +{$EXTERNALSYM CallPCREStackMalloc} + +procedure SetPCREStackFreeCallback(const Value: pcre_stack_free_callback); +{$EXTERNALSYM SetPCREStackFreeCallback} +function GetPCREStackFreeCallback: pcre_stack_free_callback; +{$EXTERNALSYM GetPCREStackFreeCallback} +procedure CallPCREStackFree(P: Pointer); +{$EXTERNALSYM CallPCREStackFree} + +procedure SetPCRECalloutCallback(const Value: pcre_callout_callback); +{$EXTERNALSYM SetPCRECalloutCallback} +function GetPCRECalloutCallback: pcre_callout_callback; +{$EXTERNALSYM GetPCRECalloutCallback} +function CallPCRECallout(var callout_block: pcre_callout_block): Integer; +{$EXTERNALSYM CallPCRECallout} + +type + TPCRELibNotLoadedHandler = procedure; cdecl; + +var + // Value to initialize function pointers below with, in case LoadPCRE fails + // or UnloadPCRE is called. Typically the handler will raise an exception. + LibNotLoadedHandler: TPCRELibNotLoadedHandler = nil; + +(* Functions *) + +// dynamic dll import +type + pcre_compile_func = function(const pattern: PAnsiChar; options: Integer; + const errptr: PPAnsiChar; erroffset: PInteger; const tableptr: PAnsiChar): PPCRE; + cdecl; + {$EXTERNALSYM pcre_compile_func} + pcre_compile2_func = function(const pattern: PAnsiChar; options: Integer; + const errorcodeptr: PInteger; const errorptr: PPAnsiChar; erroroffset: PInteger; + const tables: PAnsiChar): PPCRE; cdecl; + {$EXTERNALSYM pcre_compile2_func} + pcre_config_func = function(what: Integer; where: Pointer): Integer; + cdecl; + {$EXTERNALSYM pcre_config_func} + pcre_copy_named_substring_func = function(const code: PPCRE; const subject: PAnsiChar; + ovector: PInteger; stringcount: Integer; const stringname: PAnsiChar; + buffer: PAnsiChar; size: Integer): Integer; cdecl; + {$EXTERNALSYM pcre_copy_named_substring_func} + pcre_copy_substring_func = function(const subject: PAnsiChar; ovector: PInteger; + stringcount, stringnumber: Integer; buffer: PAnsiChar; buffersize: Integer): Integer; + cdecl; + {$EXTERNALSYM pcre_copy_substring_func} + pcre_dfa_exec_func = function(const argument_re: PPCRE; const extra_data: PPCREExtra; + const subject: PAnsiChar; length: Integer; start_offset: Integer; + options: Integer; offsets: PInteger; offsetcount: Integer; workspace: PInteger; + wscount: Integer): Integer; cdecl; + {$EXTERNALSYM pcre_dfa_exec_func} + pcre_exec_func = function(const code: PPCRE; const extra: PPCREExtra; const subject: PAnsiChar; + length, startoffset, options: Integer; ovector: PInteger; ovecsize: Integer): Integer; + cdecl; + {$EXTERNALSYM pcre_exec_func} + pcre_free_substring_func = procedure(stringptr: PAnsiChar); + cdecl; + {$EXTERNALSYM pcre_free_substring_func} + pcre_free_substring_list_func = procedure(stringptr: PPAnsiChar); + cdecl; + {$EXTERNALSYM pcre_free_substring_list_func} + pcre_fullinfo_func = function(const code: PPCRE; const extra: PPCREExtra; + what: Integer; where: Pointer): Integer; + cdecl; + {$EXTERNALSYM pcre_fullinfo_func} + pcre_get_named_substring_func = function(const code: PPCRE; const subject: PAnsiChar; + ovector: PInteger; stringcount: Integer; const stringname: PAnsiChar; + const stringptr: PPAnsiChar): Integer; cdecl; + {$EXTERNALSYM pcre_get_named_substring_func} + pcre_get_stringnumber_func = function(const code: PPCRE; + const stringname: PAnsiChar): Integer; cdecl; + {$EXTERNALSYM pcre_get_stringnumber_func} + pcre_get_stringtable_entries_func = function(const code: PPCRE; const stringname: PAnsiChar; + firstptr: PPAnsiChar; lastptr: PPAnsiChar): Integer; + cdecl; + {$EXTERNALSYM pcre_get_stringtable_entries_func} + pcre_get_substring_func = function(const subject: PAnsiChar; ovector: PInteger; + stringcount, stringnumber: Integer; const stringptr: PPAnsiChar): Integer; + cdecl; + {$EXTERNALSYM pcre_get_substring_func} + pcre_get_substring_list_func = function(const subject: PAnsiChar; ovector: PInteger; + stringcount: Integer; listptr: PPPAnsiChar): Integer; + cdecl; + {$EXTERNALSYM pcre_get_substring_list_func} + pcre_info_func = function(const code: PPCRE; optptr, firstcharptr: PInteger): Integer; + cdecl; + {$EXTERNALSYM pcre_info_func} + pcre_maketables_func = function: PAnsiChar; cdecl; + {$EXTERNALSYM pcre_maketables_func} + pcre_refcount_func = function(argument_re: PPCRE; adjust: Integer): Integer; + cdecl; + {$EXTERNALSYM pcre_refcount_func} + pcre_study_func = function(const code: PPCRE; options: Integer; const errptr: PPAnsiChar): PPCREExtra; + cdecl; + {$EXTERNALSYM pcre_study_func} + pcre_version_func = function: PAnsiChar; cdecl; + {$EXTERNALSYM pcre_version_func} + +var + pcre_compile: pcre_compile_func = nil; + {$EXTERNALSYM pcre_compile} + pcre_compile2: pcre_compile2_func = nil; + {$EXTERNALSYM pcre_compile2} + pcre_config: pcre_config_func = nil; + {$EXTERNALSYM pcre_config} + pcre_copy_named_substring: pcre_copy_named_substring_func = nil; + {$EXTERNALSYM pcre_copy_named_substring} + pcre_copy_substring: pcre_copy_substring_func = nil; + {$EXTERNALSYM pcre_copy_substring} + pcre_dfa_exec: pcre_dfa_exec_func = nil; + {$EXTERNALSYM pcre_dfa_exec} + pcre_exec: pcre_exec_func = nil; + {$EXTERNALSYM pcre_exec} + pcre_free_substring: pcre_free_substring_func = nil; + {$EXTERNALSYM pcre_free_substring} + pcre_free_substring_list: pcre_free_substring_list_func = nil; + {$EXTERNALSYM pcre_free_substring_list} + pcre_fullinfo: pcre_fullinfo_func = nil; + {$EXTERNALSYM pcre_fullinfo} + pcre_get_named_substring: pcre_get_named_substring_func = nil; + {$EXTERNALSYM pcre_get_named_substring} + pcre_get_stringnumber: pcre_get_stringnumber_func = nil; + {$EXTERNALSYM pcre_get_stringnumber} + pcre_get_stringtable_entries: pcre_get_stringtable_entries_func = nil; + {$EXTERNALSYM pcre_get_stringtable_entries} + pcre_get_substring: pcre_get_substring_func = nil; + {$EXTERNALSYM pcre_get_substring} + pcre_get_substring_list: pcre_get_substring_list_func = nil; + {$EXTERNALSYM pcre_get_substring_list} + pcre_info: pcre_info_func = nil; + {$EXTERNALSYM pcre_info} + pcre_maketables: pcre_maketables_func = nil; + {$EXTERNALSYM pcre_maketables} + pcre_refcount: pcre_refcount_func = nil; + {$EXTERNALSYM pcre_refcount} + pcre_study: pcre_study_func = nil; + {$EXTERNALSYM pcre_study} + pcre_version: pcre_version_func = nil; + {$EXTERNALSYM pcre_version} + +function IsPCRELoaded: Boolean; +function LoadPCRE: Boolean; +procedure UnloadPCRE; + +implementation + +uses + SysUtils, + {$IFDEF MSWINDOWS} + Windows; + {$ENDIF MSWINDOWS} + {$IFDEF UNIX} + {$IFDEF HAS_UNIT_TYPES} + Types, + {$ENDIF HAS_UNIT_TYPES} + {$IFDEF HAS_UNIT_LIBC} + Libc; + {$ELSE ~HAS_UNIT_LIBC} + dl; + {$ENDIF ~HAS_UNIT_LIBC} + {$ENDIF UNIX} + +type + {$IFDEF MSWINDOWS} + TModuleHandle = HINST; + {$ENDIF MSWINDOWS} + {$IFDEF LINUX} + TModuleHandle = Pointer; + {$ENDIF LINUX} + {$IFDEF DARWIN} + TModuleHandle = Pointer; + {$ENDIF DARWIN} + +const + {$IFDEF MSWINDOWS} + libpcremodulename = 'pcre3.dll'; + {$ENDIF MSWINDOWS} + {$IFDEF LINUX} + libpcremodulename = 'libpcre.so.0'; + {$ENDIF LINUX} + {$IFDEF DARWIN} + libpcremodulename = 'libpcre.dylib'; + {$ENDIF DARWIN} + PCRECompileExportName = 'pcre_compile'; + PCRECompile2ExportName = 'pcre_compile2'; + PCREConfigExportName = 'pcre_config'; + PCRECopyNamedSubstringExportName = 'pcre_copy_named_substring'; + PCRECopySubStringExportName = 'pcre_copy_substring'; + PCREDfaExecExportName = 'pcre_dfa_exec'; + PCREExecExportName = 'pcre_exec'; + PCREFreeSubStringExportName = 'pcre_free_substring'; + PCREFreeSubStringListExportName = 'pcre_free_substring_list'; + PCREFullInfoExportName = 'pcre_fullinfo'; + PCREGetNamedSubstringExportName = 'pcre_get_named_substring'; + PCREGetStringNumberExportName = 'pcre_get_stringnumber'; + PCREGetStringTableEntriesExportName = 'pcre_get_stringtable_entries'; + PCREGetSubStringExportName = 'pcre_get_substring'; + PCREGetSubStringListExportName = 'pcre_get_substring_list'; + PCREInfoExportName = 'pcre_info'; + PCREMakeTablesExportName = 'pcre_maketables'; + PCRERefCountExportName = 'pcre_refcount'; + PCREStudyExportName = 'pcre_study'; + PCREVersionExportName = 'pcre_version'; + PCREMallocExportName = 'pcre_malloc'; + PCREFreeExportName = 'pcre_free'; + PCREStackMallocExportName = 'pcre_stack_malloc'; + PCREStackFreeExportName = 'pcre_stack_free'; + PCRECalloutExportName = 'pcre_callout'; + INVALID_MODULEHANDLE_VALUE = TModuleHandle(0); + +var + PCRELib: TModuleHandle = INVALID_MODULEHANDLE_VALUE; + +procedure SetPCREMallocCallback(const Value: pcre_malloc_callback); +begin + if not Assigned(pcre_malloc_func) then + LoadPCRE; + + if Assigned(pcre_malloc_func) then + pcre_malloc_func^ := Value + else if Assigned(LibNotLoadedHandler) then + LibNotLoadedHandler; +end; + +function GetPCREMallocCallback: pcre_malloc_callback; +begin + if not Assigned(pcre_malloc_func) then + LoadPCRE; + + if not Assigned(pcre_malloc_func) then + begin + Result := nil; + if Assigned(LibNotLoadedHandler) then + LibNotLoadedHandler; + end + else + Result := pcre_malloc_func^; +end; + +function CallPCREMalloc(Size: SizeInt): Pointer; +begin + Result := pcre_malloc_func^(Size); +end; + +procedure SetPCREFreeCallback(const Value: pcre_free_callback); +begin + if not Assigned(pcre_free_func) then + LoadPCRE; + + if Assigned(pcre_free_func) then + pcre_free_func^ := Value + else if Assigned(LibNotLoadedHandler) then + LibNotLoadedHandler; +end; + +function GetPCREFreeCallback: pcre_free_callback; +begin + if not Assigned(pcre_free_func) then + LoadPCRE; + + if not Assigned(pcre_free_func) then + begin + Result := nil; + if Assigned(LibNotLoadedHandler) then + LibNotLoadedHandler; + end + else + Result := pcre_free_func^ +end; + +procedure CallPCREFree(P: Pointer); +begin + pcre_free_func^(P); +end; + +procedure SetPCREStackMallocCallback(const Value: pcre_stack_malloc_callback); +begin + if not Assigned(pcre_stack_malloc_func) then + LoadPCRE; + + if Assigned(pcre_stack_malloc_func) then + pcre_stack_malloc_func^ := Value + else if Assigned(LibNotLoadedHandler) then + LibNotLoadedHandler; +end; + +function GetPCREStackMallocCallback: pcre_stack_malloc_callback; +begin + if not Assigned(pcre_stack_malloc_func) then + LoadPCRE; + + if not Assigned(pcre_stack_malloc_func) then + begin + Result := nil; + if Assigned(LibNotLoadedHandler) then + LibNotLoadedHandler; + end + else + Result := pcre_stack_malloc_func^; +end; + +function CallPCREStackMalloc(Size: SizeInt): Pointer; +begin + Result := pcre_stack_malloc_func^(Size); +end; + +procedure SetPCREStackFreeCallback(const Value: pcre_stack_free_callback); +begin + if not Assigned(pcre_stack_free_func) then + LoadPCRE; + + if Assigned(pcre_stack_free_func) then + pcre_stack_free_func^ := Value + else if Assigned(LibNotLoadedHandler) then + LibNotLoadedHandler; +end; + +function GetPCREStackFreeCallback: pcre_stack_free_callback; +begin + if not Assigned(pcre_stack_free_func) then + LoadPCRE; + + if not Assigned(pcre_stack_free_func) then + begin + Result := nil; + if Assigned(LibNotLoadedHandler) then + LibNotLoadedHandler; + end + else + Result := pcre_stack_free_func^; +end; + +procedure CallPCREStackFree(P: Pointer); +begin + pcre_stack_free_func^(P); +end; + +procedure SetPCRECalloutCallback(const Value: pcre_callout_callback); +begin + if not Assigned(pcre_callout_func) then + LoadPCRE; + + if Assigned(pcre_callout_func) then + pcre_callout_func^ := Value + else if Assigned(LibNotLoadedHandler) then + LibNotLoadedHandler; +end; + +function GetPCRECalloutCallback: pcre_callout_callback; +begin + if not Assigned(pcre_callout_func) then + LoadPCRE; + + if not Assigned(pcre_callout_func) then + begin + Result := nil; + if Assigned(LibNotLoadedHandler) then + LibNotLoadedHandler; + end + else + Result := pcre_callout_func^; +end; + +function CallPCRECallout(var callout_block: pcre_callout_block): Integer; +begin + Result := pcre_callout_func^(callout_block); +end; + +procedure InitPCREFuncPtrs(const Value: Pointer); +begin + @pcre_compile := Value; + @pcre_compile2 := Value; + @pcre_config := Value; + @pcre_copy_named_substring := Value; + @pcre_copy_substring := Value; + @pcre_dfa_exec := Value; + @pcre_exec := Value; + @pcre_free_substring := Value; + @pcre_free_substring_list := Value; + @pcre_fullinfo := Value; + @pcre_get_named_substring := Value; + @pcre_get_stringnumber := Value; + @pcre_get_stringtable_entries := Value; + @pcre_get_substring := Value; + @pcre_get_substring_list := Value; + @pcre_info := Value; + @pcre_maketables := Value; + @pcre_refcount := Value; + @pcre_study := Value; + @pcre_version := Value; + pcre_malloc_func := nil; + pcre_free_func := nil; + pcre_stack_malloc_func := nil; + pcre_stack_free_func := nil; + pcre_callout_func := nil; +end; + +function IsPCRELoaded: Boolean; +begin + Result := PCRELib <> INVALID_MODULEHANDLE_VALUE; +end; + +function LoadPCRE: Boolean; + function GetSymbol(SymbolName: PAnsiChar): Pointer; + begin + {$IFDEF MSWINDOWS} + Result := GetProcAddress(PCRELib, SymbolName); + {$ENDIF MSWINDOWS} + {$IFDEF UNIX} + Result := dlsym(PCRELib, SymbolName); + {$ENDIF UNIX} + end; + +begin + Result := PCRELib <> INVALID_MODULEHANDLE_VALUE; + if Result then + Exit; + + if PCRELib = INVALID_MODULEHANDLE_VALUE then + {$IFDEF MSWINDOWS} + PCRELib := SafeLoadLibrary(libpcremodulename); + {$ENDIF MSWINDOWS} + {$IFDEF UNIX} + PCRELib := dlopen(PAnsiChar(libpcremodulename), RTLD_NOW); + {$ENDIF UNIX} + Result := PCRELib <> INVALID_MODULEHANDLE_VALUE; + if Result then + begin + @pcre_compile := GetSymbol(PCRECompileExportName); + @pcre_compile2 := GetSymbol(PCRECompile2ExportName); + @pcre_config := GetSymbol(PCREConfigExportName); + @pcre_copy_named_substring := GetSymbol(PCRECopyNamedSubstringExportName); + @pcre_copy_substring := GetSymbol(PCRECopySubStringExportName); + @pcre_dfa_exec := GetSymbol(PCREDfaExecExportName); + @pcre_exec := GetSymbol(PCREExecExportName); + @pcre_free_substring := GetSymbol(PCREFreeSubStringExportName); + @pcre_free_substring_list := GetSymbol(PCREFreeSubStringListExportName); + @pcre_fullinfo := GetSymbol(PCREFullInfoExportName); + @pcre_get_named_substring := GetSymbol(PCREGetNamedSubstringExportName); + @pcre_get_stringnumber := GetSymbol(PCREGetStringNumberExportName); + @pcre_get_stringtable_entries := GetSymbol(PCREGetStringTableEntriesExportName); + @pcre_get_substring := GetSymbol(PCREGetSubStringExportName); + @pcre_get_substring_list := GetSymbol(PCREGetSubStringListExportName); + @pcre_info := GetSymbol(PCREInfoExportName); + @pcre_maketables := GetSymbol(PCREMakeTablesExportName); + @pcre_refcount := GetSymbol(PCRERefCountExportName); + @pcre_study := GetSymbol(PCREStudyExportName); + @pcre_version := GetSymbol(PCREVersionExportName); + pcre_malloc_func := GetSymbol(PCREMallocExportName); + pcre_free_func := GetSymbol(PCREFreeExportName); + pcre_stack_malloc_func := GetSymbol(PCREStackMallocExportName); + pcre_stack_free_func := GetSymbol(PCREStackFreeExportName); + pcre_callout_func := GetSymbol(PCRECalloutExportName); + end + else + InitPCREFuncPtrs(@LibNotLoadedHandler); +end; + +procedure UnloadPCRE; +begin + if PCRELib <> INVALID_MODULEHANDLE_VALUE then + {$IFDEF MSWINDOWS} + FreeLibrary(PCRELib); + {$ENDIF MSWINDOWS} + {$IFDEF UNIX} + dlclose(Pointer(PCRELib)); + {$ENDIF UNIX} + PCRELib := INVALID_MODULEHANDLE_VALUE; + InitPCREFuncPtrs(@LibNotLoadedHandler); +end; + +(* +function pcre_compile; external libpcremodulename name PCRECompileExportName; +function pcre_compile2; external libpcremodulename name PCRECompile2ExportName; +function pcre_config; external libpcremodulename name PCREConfigExportName; +function pcre_copy_named_substring; external libpcremodulename name PCRECopyNamedSubStringExportName; +function pcre_copy_substring; external libpcremodulename name PCRECopySubStringExportName; +function pcre_dfa_exec; external libpcremodulename name PCREDfaExecExportName; +function pcre_exec; external libpcremodulename name PCREExecExportName; +procedure pcre_free_substring; external libpcremodulename name PCREFreeSubStringExportName; +procedure pcre_free_substring_list; external libpcremodulename name PCREFreeSubStringListExportName; +function pcre_fullinfo; external libpcremodulename name PCREFullInfoExportName; +function pcre_get_named_substring; external libpcremodulename name PCREGetNamedSubStringExportName; +function pcre_get_stringnumber; external libpcremodulename name PCREGetStringNumberExportName; +function pcre_get_stringtable_entries; external libpcremodulename name PCREGetStringTableEntriesExportName; +function pcre_get_substring; external libpcremodulename name PCREGetSubStringExportName; +function pcre_get_substring_list; external libpcremodulename name PCREGetSubStringListExportName; +function pcre_info; external libpcremodulename name PCREInfoExportName; +function pcre_maketables; external libpcremodulename name PCREMakeTablesExportName; +function pcre_refcount; external libpcremodulename name PCRERefCountExportName; +function pcre_study; external libpcremodulename name PCREStudyExportName; +function pcre_version; external libpcremodulename name PCREVersionExportName; +*) + +end. diff --git a/Lua/src/lib/portaudio/portaudio.pas b/Lua/src/lib/portaudio/portaudio.pas index a0286b48..ea7d06b7 100644 --- a/Lua/src/lib/portaudio/portaudio.pas +++ b/Lua/src/lib/portaudio/portaudio.pas @@ -109,8 +109,8 @@ type TPaErrorCode = {enum}cint; const paStreamIsNotStopped = (paNotInitialized+18); paInputOverflowed = (paNotInitialized+19); paOutputUnderflowed = (paNotInitialized+20); - paHostApiNotFound = (paNotInitialized+21); - paInvalidHostApi = (paNotInitialized+22); + paHostApiNotFound = (paNotInitialized+21); // The notes below are from the + paInvalidHostApi = (paNotInitialized+22); // original file portaudio.h paCanNotReadFromACallbackStream = (paNotInitialized+23); {**< @todo review error code name *} paCanNotWriteToACallbackStream = (paNotInitialized+24); {**< @todo review error code name *} paCanNotReadFromAnOutputOnlyStream = (paNotInitialized+25); {**< @todo review error code name *} diff --git a/Lua/src/lib/projectM/projectM.pas b/Lua/src/lib/projectM/projectM.pas index 4adba17d..533cb19b 100644 --- a/Lua/src/lib/projectM/projectM.pas +++ b/Lua/src/lib/projectM/projectM.pas @@ -2,7 +2,7 @@ unit projectM; {$IFDEF FPC} {$MODE DELPHI} - {$H+} (* use AnsiString *) + {$H+} (* use long strings *) {$PACKENUM 4} (* use 4-byte enums *) {$PACKRECORDS C} (* C/C++-compatible record packing *) {$ELSE} diff --git a/Lua/src/lib/zlib/zlib.pas b/Lua/src/lib/zlib/zlib.pas index 31d6a68b..8d09313f 100644 --- a/Lua/src/lib/zlib/zlib.pas +++ b/Lua/src/lib/zlib/zlib.pas @@ -14,7 +14,7 @@ interface {$ifdef FPC} {$mode objfpc} // Needed for array of const - {$H+} // use AnsiString + {$H+} // use long strings {$PACKRECORDS C} {$endif} diff --git a/Lua/src/lua/ULuaCore.pas b/Lua/src/lua/ULuaCore.pas index fc6ac49c..d3231ae2 100644 --- a/Lua/src/lua/ULuaCore.pas +++ b/Lua/src/lua/ULuaCore.pas @@ -33,7 +33,7 @@ interface {$I switches.inc}
-uses SysUtils, ULua, UHookableEvent;
+uses SysUtils, ULua, UHookableEvent, UPath;
type
{ this exception is raised when the lua panic function
@@ -59,7 +59,7 @@ type TLuaPlugin = class
private
iId: Integer;
- Filename: WideString;
+ Filename: IPath;
State: Plua_State; //< all functions of this plugin are called with this Lua state
bPaused: Boolean; //< If true no lua functions from this state are called
ErrorCount: Integer; //< counts the errors that occured during function calls of this plugin
@@ -72,7 +72,7 @@ type sStatus: TLuaPlugin_Status;
public
- constructor Create(Filename: WideString; Id: Integer);
+ constructor Create(Filename: IPath; Id: Integer);
property Id: Integer read iId;
property Name: String read sName;
@@ -132,8 +132,8 @@ type procedure LoadPlugins; //< calls LoadPlugin w/ Plugindir and LoadingFinished Eventchain
- procedure BrowseDir(Dir: WideString); //< searches for files w/ extension .usdx in the specified dir and tries to load them w/ lua
- procedure LoadPlugin(Filename: WideString); //< tries to load filename w/ lua and creates the default usdx lua environment for the plugins state
+ procedure BrowseDir(Dir: IPath); //< searches for files w/ extension .usdx in the specified dir and tries to load them w/ lua
+ procedure LoadPlugin(Filename: IPath); //< tries to load filename w/ lua and creates the default usdx lua environment for the plugins state
function GetPluginByName(Name: String): TLuaPlugin;
function GetPluginById(Id: Integer): TLuaPlugin;
@@ -195,7 +195,7 @@ var LuaCore: TLuaCore;
implementation
-uses ULog, UPlatform, ULuaUsdx, UMain, StrUtils;
+uses ULog, UFilesystem, ULuaUsdx, UPathUtils, StrUtils;
constructor TLuaCore.Create;
begin
@@ -242,27 +242,39 @@ end; { searches for files w/ extension .usdx in the specified
dir and tries to load them w/ lua }
-procedure TLuaCore.BrowseDir(Dir: WideString);
+procedure TLuaCore.BrowseDir(Dir: IPath);
var
- Files: TDirectoryEntryArray;
- I: Integer;
+ Iter: IFileIterator;
+ FileInfo: TFileInfo;
+ FileName: IPath;
+ Ext: IPath;
begin
- try
- Files := Platform.DirectoryFindFiles(Dir, '.usdx', true);
-
- for I := 0 to High(Files) do
- if (Files[I].IsDirectory) then
- BrowseDir(Dir + Files[i].Name) //browse recursive
- else if (Files[I].IsFile) then
- LoadPlugin(Dir + Files[i].Name);
- except
- Log.LogError('Couldn''t deal with directory/file: ' + Dir + ' in TLuaCore.BrowseDir')
+ Ext := Path('.usdx');
+
+ // search for all files and directories
+ Iter := FileSystem.FileFind(Dir.Append('*'), faAnyFile);
+ while (Iter.HasNext) do
+ begin
+ FileInfo := Iter.Next;
+ FileName := FileInfo.Name;
+ if ((FileInfo.Attr and faDirectory) <> 0) then
+ begin
+ if (not FileName.Equals('.')) and (not FileName.Equals('..')) then
+ BrowseDir(Dir.Append(FileName));
+ end
+ else
+ begin
+ if (Ext.Equals(FileName.GetExtension(), true)) then
+ begin
+ LoadPlugin(Dir.Append(FileName));
+ end;
+ end;
end;
end;
{ tries to load filename w/ lua and creates the default
usdx lua environment for the plugins state }
-procedure TLuaCore.LoadPlugin(Filename: WideString);
+procedure TLuaCore.LoadPlugin(Filename: IPath);
var
Len: Integer;
begin
@@ -676,7 +688,7 @@ end; // Implementation of TLuaPlugin
//--------
-constructor TLuaPlugin.Create(Filename: WideString; Id: Integer);
+constructor TLuaPlugin.Create(Filename: IPath; Id: Integer);
begin
inherited Create;
Self.iId := Id;
@@ -701,6 +713,8 @@ end; { does the main loading part
can not be called by create, because Plugins[Id] isn't defined there }
procedure TLuaPlugin.Load;
+ var
+ Filename: String;
begin
// create Lua state for this plugin
State := luaL_newstate;
@@ -709,7 +723,11 @@ begin //we don't expect
lua_atPanic(State, TLua_CustomPanic);
- if (luaL_loadfile(State, PChar(String(Self.Filename))) = 0) then
+ Filename := Self.Filename.ToNative;
+ // to-do : fix this
+ // it may solve the problem if we read the file
+ // and pass lua the text }
+ if ({luaL_loadfile(State, PChar(Filename))}1 = 0) then
begin // file loaded successful
{ note: we run the file here, but the environment isn't
set up now. it just causes the functions to
@@ -719,7 +737,7 @@ begin with require, this code would be useless. }
if (lua_pcall(State, 0, 0, 0) = 0) then
begin // file called successful
-
+
//let the core prepare our state
LuaCore.PrepareState(State);
@@ -749,7 +767,7 @@ begin else
begin
sStatus := psErrorInInit;
- Log.LogError('error in plugin_init: ' + Filename, 'lua');
+ Log.LogError('error in plugin_init: ' + Self.Filename.ToUTF8, 'lua');
Unload;
end;
end
@@ -757,7 +775,7 @@ begin begin
sStatus := psErrorOnLoad;
Log.LogError(String(lua_toString(State, 1)), 'lua');
- Log.LogError('unable to call file: ' + Filename, 'lua');
+ Log.LogError('unable to call file: ' + Self.Filename.ToUTF8, 'lua');
Unload;
end;
@@ -766,7 +784,7 @@ begin begin
sStatus := psErrorOnLoad;
Log.LogError(String(lua_toString(State, 1)), 'lua');
- Log.LogError('unable to load file: ' + Filename, 'lua');
+ Log.LogError('unable to load file: ' + Self.Filename.ToUTF8, 'lua');
Unload;
end;
end;
@@ -855,7 +873,7 @@ begin end
else
begin
- Log.LogError('trying to call function of closed or not opened lua state', IfThen(HasRegistred, Name, Filename));
+ Log.LogError('trying to call function of closed or not opened lua state', IfThen(HasRegistred, Name, Filename.ToUTF8));
end;
end;
diff --git a/Lua/src/lua/ULuaScreenSing.pas b/Lua/src/lua/ULuaScreenSing.pas index cdf83e35..7e17224c 100644 --- a/Lua/src/lua/ULuaScreenSing.pas +++ b/Lua/src/lua/ULuaScreenSing.pas @@ -114,7 +114,7 @@ const );
implementation
-uses UScreenSing, UMain, UDisplay, UGraphic, UMusic, ULuaUtils, SysUtils;
+uses UScreenSing, UNote, UDisplay, UGraphic, UMusic, ULuaUtils, SysUtils;
{ returns a table with following structure:
t[1..playercount] = score of player i }
diff --git a/Lua/src/macosx/PseudoThread.pas b/Lua/src/macosx/PseudoThread.pas index 5764395d..d74285f7 100644 --- a/Lua/src/macosx/PseudoThread.pas +++ b/Lua/src/macosx/PseudoThread.pas @@ -37,23 +37,24 @@ type // Debugging threads with XCode doesn't seem to work. // We use PseudoThread in Debug mode to get proper debugging. + TPseudoThread = class(TObject) private protected Terminated, - FreeOnTerminate : Boolean; + FreeOnTerminate: boolean; procedure Execute; virtual; abstract; procedure Resume; procedure Suspend; public - constructor Create(const suspended : Boolean); + constructor Create(const suspended : boolean); end; implementation { TPseudoThread } -constructor TPseudoThread.Create(const suspended : Boolean); +constructor TPseudoThread.Create(const suspended: boolean); begin if not suspended then begin diff --git a/Lua/src/macosx/Windows.pas b/Lua/src/macosx/Windows.pas deleted file mode 100644 index 28b5c99f..00000000 --- a/Lua/src/macosx/Windows.pas +++ /dev/null @@ -1,194 +0,0 @@ -{* 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 Windows; - -{$I switches.inc} - -interface - -uses - Types; - -const - opengl32 = 'OpenGL'; - MAX_PATH = 260; - -type - - DWORD = Types.DWORD; - {$EXTERNALSYM DWORD} - BOOL = LongBool; - {$EXTERNALSYM BOOL} - PBOOL = ^BOOL; - {$EXTERNALSYM PBOOL} - PByte = Types.PByte; - PINT = ^Integer; - {$EXTERNALSYM PINT} - PSingle = ^Single; - PWORD = ^Word; - {$EXTERNALSYM PWORD} - PDWORD = ^DWORD; - {$EXTERNALSYM PDWORD} - LPDWORD = PDWORD; - {$EXTERNALSYM LPDWORD} - HDC = type LongWord; - {$EXTERNALSYM HDC} - HGLRC = type LongWord; - {$EXTERNALSYM HGLRC} - TLargeInteger = Int64; - HFONT = type LongWord; - {$EXTERNALSYM HFONT} - HWND = type LongWord; - {$EXTERNALSYM HWND} - - PPaletteEntry = ^TPaletteEntry; - {$EXTERNALSYM tagPALETTEENTRY} - tagPALETTEENTRY = packed record - peRed: Byte; - peGreen: Byte; - peBlue: Byte; - peFlags: Byte; - end; - TPaletteEntry = tagPALETTEENTRY; - {$EXTERNALSYM PALETTEENTRY} - PALETTEENTRY = tagPALETTEENTRY; - - PRGBQuad = ^TRGBQuad; - {$EXTERNALSYM tagRGBQUAD} - tagRGBQUAD = packed record - rgbBlue: Byte; - rgbGreen: Byte; - rgbRed: Byte; - rgbReserved: Byte; - end; - TRGBQuad = tagRGBQUAD; - {$EXTERNALSYM RGBQUAD} - RGBQUAD = tagRGBQUAD; - - PBitmapInfoHeader = ^TBitmapInfoHeader; - {$EXTERNALSYM tagBITMAPINFOHEADER} - tagBITMAPINFOHEADER = packed record - biSize: DWORD; - biWidth: Longint; - biHeight: Longint; - biPlanes: Word; - biBitCount: Word; - biCompression: DWORD; - biSizeImage: DWORD; - biXPelsPerMeter: Longint; - biYPelsPerMeter: Longint; - biClrUsed: DWORD; - biClrImportant: DWORD; - end; - TBitmapInfoHeader = tagBITMAPINFOHEADER; - {$EXTERNALSYM BITMAPINFOHEADER} - BITMAPINFOHEADER = tagBITMAPINFOHEADER; - - PBitmapInfo = ^TBitmapInfo; - {$EXTERNALSYM tagBITMAPINFO} - tagBITMAPINFO = packed record - bmiHeader: TBitmapInfoHeader; - bmiColors: array[0..0] of TRGBQuad; - end; - TBitmapInfo = tagBITMAPINFO; - {$EXTERNALSYM BITMAPINFO} - BITMAPINFO = tagBITMAPINFO; - - PBitmapFileHeader = ^TBitmapFileHeader; - {$EXTERNALSYM tagBITMAPFILEHEADER} - tagBITMAPFILEHEADER = packed record - bfType: Word; - bfSize: DWORD; - bfReserved1: Word; - bfReserved2: Word; - bfOffBits: DWORD; - end; - TBitmapFileHeader = tagBITMAPFILEHEADER; - {$EXTERNALSYM BITMAPFILEHEADER} - BITMAPFILEHEADER = tagBITMAPFILEHEADER; - - -function MakeLong(a, b: Word): Longint; -procedure ZeroMemory(Destination: Pointer; Length: DWORD); -function QueryPerformanceFrequency(var lpFrequency: TLargeInteger): BOOL; -function QueryPerformanceCounter(var lpPerformanceCount: TLargeInteger): BOOL; -function GetTickCount : Cardinal; -Procedure ShowMessage(msg : String); -procedure CopyMemory(Destination: Pointer; Source: Pointer; Length: DWORD); - -implementation - -uses - SDL; - -procedure CopyMemory(Destination: Pointer; Source: Pointer; Length: DWORD); -begin - Move(Source^, Destination^, Length); -end; - -procedure ShowMessage(msg : String); -begin - // to be implemented -end; - -function MakeLong(A, B: Word): Longint; -begin - Result := (LongInt(B) shl 16) + A; -end; - -procedure ZeroMemory(Destination: Pointer; Length: DWORD); -begin - FillChar( Destination^, Length, 0); -end; - -function QueryPerformanceFrequency(var lpFrequency: TLargeInteger): BOOL; -begin -{$IFDEF MSWINDOWS} - Result := Windows.QueryPerformanceFrequency(lpFrequency); -{$ENDIF} -{$IFDEF MACOS} - Result := true; - lpFrequency := 1000; -{$ENDIF} -end; - -function QueryPerformanceCounter(var lpPerformanceCount: TLargeInteger): BOOL; -begin -{$IFDEF MSWINDOWS} - Result := Windows.QueryPerformanceCounter(lpPerformanceCount); -{$ENDIF} -{$IFDEF MACOS} - Result := true; - lpPerformanceCount := SDL_GetTicks; -{$ENDIF} -end; - -function GetTickCount : Cardinal; -begin - Result := SDL_GetTicks; -end; - -end. diff --git a/Lua/src/media/UAudioConverter.pas b/Lua/src/media/UAudioConverter.pas index 24131b16..657b80dd 100644 --- a/Lua/src/media/UAudioConverter.pas +++ b/Lua/src/media/UAudioConverter.pas @@ -70,7 +70,7 @@ type function Init(SrcFormatInfo: TAudioFormatInfo; DstFormatInfo: TAudioFormatInfo): boolean; override; destructor Destroy(); override; - function Convert(InputBuffer: PChar; OutputBuffer: PChar; var InputSize: integer): integer; override; + function Convert(InputBuffer: PByteArray; OutputBuffer: PByteArray; var InputSize: integer): integer; override; function GetOutputBufferSize(InputSize: integer): integer; override; function GetRatio(): double; override; end; @@ -87,7 +87,7 @@ type function Init(SrcFormatInfo: TAudioFormatInfo; DstFormatInfo: TAudioFormatInfo): boolean; override; destructor Destroy(); override; - function Convert(InputBuffer: PChar; OutputBuffer: PChar; var InputSize: integer): integer; override; + function Convert(InputBuffer: PByteArray; OutputBuffer: PByteArray; var InputSize: integer): integer; override; function GetOutputBufferSize(InputSize: integer): integer; override; function GetRatio(): double; override; end; @@ -103,7 +103,7 @@ type function Init(SrcFormatInfo: TAudioFormatInfo; DstFormatInfo: TAudioFormatInfo): boolean; override; destructor Destroy(); override; - function Convert(InputBuffer: PChar; OutputBuffer: PChar; var InputSize: integer): integer; override; + function Convert(InputBuffer: PByteArray; OutputBuffer: PByteArray; var InputSize: integer): integer; override; function GetOutputBufferSize(InputSize: integer): integer; override; function GetRatio(): double; override; end; @@ -173,7 +173,7 @@ begin Result := cvt.len_ratio; end; -function TAudioConverter_SDL.Convert(InputBuffer: PChar; OutputBuffer: PChar; var InputSize: integer): integer; +function TAudioConverter_SDL.Convert(InputBuffer: PByteArray; OutputBuffer: PByteArray; var InputSize: integer): integer; begin Result := -1; @@ -242,7 +242,7 @@ begin inherited; end; -function TAudioConverter_FFmpeg.Convert(InputBuffer: PChar; OutputBuffer: PChar; var InputSize: integer): integer; +function TAudioConverter_FFmpeg.Convert(InputBuffer: PByteArray; OutputBuffer: PByteArray; var InputSize: integer): integer; var InputSampleCount: integer; OutputSampleCount: integer; @@ -360,11 +360,11 @@ begin inherited; end; -function TAudioConverter_SRC.Convert(InputBuffer: PChar; OutputBuffer: PChar; var InputSize: integer): integer; +function TAudioConverter_SRC.Convert(InputBuffer: PByteArray; OutputBuffer: PByteArray; var InputSize: integer): integer; var FloatInputBuffer: PSingle; FloatOutputBuffer: PSingle; - TempBuffer: PChar; + TempBuffer: PByteArray; TempSize: integer; NumSamples: integer; OutputSize: integer; diff --git a/Lua/src/media/UAudioCore_Bass.pas b/Lua/src/media/UAudioCore_Bass.pas index 12623dc1..197f9760 100644 --- a/Lua/src/media/UAudioCore_Bass.pas +++ b/Lua/src/media/UAudioCore_Bass.pas @@ -44,6 +44,7 @@ type public constructor Create(); class function GetInstance(): TAudioCore_Bass; + function CheckVersion(): boolean; function ErrorGetString(): string; overload; function ErrorGetString(errCode: integer): string; overload; function ConvertAudioFormatToBASSFlags(Format: TAudioSampleFormat; out Flags: DWORD): boolean; @@ -56,6 +57,12 @@ uses UMain, ULog; +const + // TODO: 2.4.2 is not ABI compatible with older versions + // as (BASS_RECORDINFO.driver was removed) + //BASS_MIN_REQUIRED_VERSION = $02040201; + BASS_MIN_REQUIRED_VERSION = $02000000; + var Instance: TAudioCore_Bass; @@ -71,6 +78,11 @@ begin Result := Instance; end; +function TAudioCore_Bass.CheckVersion(): boolean; +begin + Result := BASS_GetVersion() >= BASS_MIN_REQUIRED_VERSION; +end; + function TAudioCore_Bass.ErrorGetString(): string; begin Result := ErrorGetString(BASS_ErrorGetCode()); diff --git a/Lua/src/media/UAudioDecoder_Bass.pas b/Lua/src/media/UAudioDecoder_Bass.pas index 3c31175d..d6d2425a 100644 --- a/Lua/src/media/UAudioDecoder_Bass.pas +++ b/Lua/src/media/UAudioDecoder_Bass.pas @@ -38,11 +38,12 @@ implementation uses Classes, SysUtils, + bass, UMain, UMusic, UAudioCore_Bass, ULog, - bass; + UPath; type TBassDecodeStream = class(TAudioDecodeStream) @@ -65,7 +66,7 @@ type function IsEOF(): boolean; override; function IsError(): boolean; override; - function ReadData(Buffer: PChar; BufSize: integer): integer; override; + function ReadData(Buffer: PByteArray; BufSize: integer): integer; override; end; type @@ -75,7 +76,7 @@ type function InitializeDecoder(): boolean; function FinalizeDecoder(): boolean; - function Open(const Filename: string): TAudioDecodeStream; + function Open(const Filename: IPath): TAudioDecodeStream; end; var @@ -193,7 +194,7 @@ begin Result := Error; end; -function TBassDecodeStream.ReadData(Buffer: PChar; BufSize: integer): integer; +function TBassDecodeStream.ReadData(Buffer: PByteArray; BufSize: integer): integer; begin Result := BASS_ChannelGetData(Handle, Buffer, BufSize); // check error state (do not handle EOF as error) @@ -213,7 +214,10 @@ end; function TAudioDecoder_Bass.InitializeDecoder(): boolean; begin + Result := false; BassCore := TAudioCore_Bass.GetInstance(); + if not BassCore.CheckVersion then + Exit; Result := true; end; @@ -222,7 +226,7 @@ begin Result := true; end; -function TAudioDecoder_Bass.Open(const Filename: string): TAudioDecodeStream; +function TAudioDecoder_Bass.Open(const Filename: IPath): TAudioDecodeStream; var Stream: HSTREAM; ChannelInfo: BASS_CHANNELINFO; @@ -237,7 +241,14 @@ begin // TODO: use BASS_STREAM_PRESCAN for accurate seeking in VBR-files? // disadvantage: seeking will slow down. - Stream := BASS_StreamCreateFile(False, PChar(Filename), 0, 0, BASS_STREAM_DECODE); + + {$IFDEF MSWINDOWS} + // Windows: Use UTF-16 version + Stream := BASS_StreamCreateFile(False, PWideChar(Filename.ToWide), 0, 0, BASS_STREAM_DECODE or BASS_UNICODE); + {$ELSE} + // Mac OS X: Use UTF8/ANSI version + Stream := BASS_StreamCreateFile(False, PAnsiChar(Filename.ToNative), 0, 0, BASS_STREAM_DECODE); + {$ENDIF} if (Stream = 0) then begin //Log.LogError(BassCore.ErrorGetString(), 'TAudioDecoder_Bass.Open'); @@ -247,7 +258,7 @@ begin // check if BASS opened some erroneously recognized file-formats if BASS_ChannelGetInfo(Stream, channelInfo) then begin - fileExt := ExtractFileExt(Filename); + fileExt := Filename.GetExtension.ToUTF8; // BASS opens FLV-files (maybe others too) although it cannot handle them. // Setting BASS_CONFIG_VERIFY to the max. value (100000) does not help. if ((fileExt = '.flv') and (channelInfo.ctype = BASS_CTYPE_STREAM_MP1)) then diff --git a/Lua/src/media/UAudioDecoder_FFmpeg.pas b/Lua/src/media/UAudioDecoder_FFmpeg.pas index 399c2f52..d079afdc 100644 --- a/Lua/src/media/UAudioDecoder_FFmpeg.pas +++ b/Lua/src/media/UAudioDecoder_FFmpeg.pas @@ -56,23 +56,24 @@ interface implementation uses + SDL, // SDL redefines some base types -> include before SysUtils to ignore them Classes, - SysUtils, Math, - UMusic, - UIni, - UMain, + SysUtils, avcodec, avformat, avutil, avio, mathematics, // used for av_rescale_q rational, + UMusic, + UIni, + UMain, UMediaCore_FFmpeg, - SDL, ULog, UCommon, - UConfig; + UConfig, + UPath; const MAX_AUDIOQ_SIZE = (5 * 16 * 1024); @@ -129,16 +130,16 @@ type // state-vars for DecodeFrame (locked by DecoderLock) AudioPaket: TAVPacket; - AudioPaketData: PChar; + AudioPaketData: PByteArray; AudioPaketSize: integer; AudioPaketSilence: integer; // number of bytes of silence to return // state-vars for AudioCallback (locked by DecoderLock) AudioBufferPos: integer; AudioBufferSize: integer; - AudioBuffer: PChar; + AudioBuffer: PByteArray; - Filename: string; + Filename: IPath; procedure SetPositionIntern(Time: real; Flush: boolean; Blocking: boolean); procedure SetEOF(State: boolean); {$IFDEF HasInline}inline;{$ENDIF} @@ -153,7 +154,7 @@ type procedure PauseParser(); procedure ResumeParser(); - function DecodeFrame(Buffer: PChar; BufferSize: integer): integer; + function DecodeFrame(Buffer: PByteArray; BufferSize: integer): integer; procedure FlushCodecBuffers(); procedure PauseDecoder(); procedure ResumeDecoder(); @@ -161,7 +162,7 @@ type constructor Create(); destructor Destroy(); override; - function Open(const Filename: string): boolean; + function Open(const Filename: IPath): boolean; procedure Close(); override; function GetLength(): real; override; @@ -173,17 +174,17 @@ type function IsEOF(): boolean; override; function IsError(): boolean; override; - function ReadData(Buffer: PChar; BufferSize: integer): integer; override; + function ReadData(Buffer: PByteArray; BufferSize: integer): integer; override; end; type - TAudioDecoder_FFmpeg = class( TInterfacedObject, IAudioDecoder ) + TAudioDecoder_FFmpeg = class(TInterfacedObject, IAudioDecoder) public function GetName: string; function InitializeDecoder(): boolean; function FinalizeDecoder(): boolean; - function Open(const Filename: string): TAudioDecodeStream; + function Open(const Filename: IPath): TAudioDecodeStream; end; var @@ -270,7 +271,7 @@ begin inherited; end; -function TFFmpegDecodeStream.Open(const Filename: string): boolean; +function TFFmpegDecodeStream.Open(const Filename: IPath): boolean; var SampleFormat: TAudioSampleFormat; AVResult: integer; @@ -280,18 +281,18 @@ begin Close(); Reset(); - if (not FileExists(Filename)) then + if (not Filename.IsFile) then begin - Log.LogError('Audio-file does not exist: "' + Filename + '"', 'UAudio_FFmpeg'); + Log.LogError('Audio-file does not exist: "' + Filename.ToNative + '"', 'UAudio_FFmpeg'); Exit; end; Self.Filename := Filename; - // open audio file - if (av_open_input_file(FormatCtx, PChar(Filename), nil, 0, nil) <> 0) then + // use custom 'ufile' protocol for UTF-8 support + if (av_open_input_file(FormatCtx, PAnsiChar('ufile:'+FileName.ToUTF8), nil, 0, nil) <> 0) then begin - Log.LogError('av_open_input_file failed: "' + Filename + '"', 'UAudio_FFmpeg'); + Log.LogError('av_open_input_file failed: "' + Filename.ToNative + '"', 'UAudio_FFmpeg'); Exit; end; @@ -301,7 +302,7 @@ begin // retrieve stream information if (av_find_stream_info(FormatCtx) < 0) then begin - Log.LogError('av_find_stream_info failed: "' + Filename + '"', 'UAudio_FFmpeg'); + Log.LogError('av_find_stream_info failed: "' + Filename.ToNative + '"', 'UAudio_FFmpeg'); Close(); Exit; end; @@ -310,13 +311,13 @@ begin FormatCtx^.pb.eof_reached := 0; {$IFDEF DebugFFmpegDecode} - dump_format(FormatCtx, 0, pchar(Filename), 0); + dump_format(FormatCtx, 0, PAnsiChar(Filename.ToNative), 0); {$ENDIF} AudioStreamIndex := FFmpegCore.FindAudioStreamIndex(FormatCtx); if (AudioStreamIndex < 0) then begin - Log.LogError('FindAudioStreamIndex: No Audio-stream found "' + Filename + '"', 'UAudio_FFmpeg'); + Log.LogError('FindAudioStreamIndex: No Audio-stream found "' + Filename.ToNative + '"', 'UAudio_FFmpeg'); Close(); Exit; end; @@ -378,14 +379,14 @@ begin // try standard format SampleFormat := asfS16; end; - + if CodecCtx^.channels > 255 then + Log.LogStatus('Error: CodecCtx^.channels > 255', 'TFFmpegDecodeStream.Open'); FormatInfo := TAudioFormatInfo.Create( - CodecCtx^.channels, + byte(CodecCtx^.channels), CodecCtx^.sample_rate, SampleFormat ); - PacketQueue := TPacketQueue.Create(); // finally start the decode thread @@ -446,7 +447,9 @@ end; function TFFmpegDecodeStream.GetLength(): real; begin - // do not forget to consider the start_time value here + // do not forget to consider the start_time value here + // there is a type size mismatch warnign because start_time and duration are cint64. + // So, in principle there could be an overflow when doing the sum. Result := (FormatCtx^.start_time + FormatCtx^.duration) / AV_TIME_BASE; end; @@ -643,7 +646,6 @@ end; function TFFmpegDecodeStream.ParseLoop(): boolean; var Packet: TAVPacket; - StatusPacket: PAVPacket; SeekTarget: int64; ByteIOCtx: PByteIOContext; ErrorCode: integer; @@ -862,7 +864,7 @@ begin end; end; -function TFFmpegDecodeStream.DecodeFrame(Buffer: PChar; BufferSize: integer): integer; +function TFFmpegDecodeStream.DecodeFrame(Buffer: PByteArray; BufferSize: integer): integer; var PaketDecodedSize: integer; // size of packet data used for decoding DataSize: integer; // size of output data decoded by FFmpeg @@ -945,7 +947,7 @@ begin Exit; // handle Status-packet - if (PChar(AudioPaket.data) = STATUS_PACKET) then + if (PAnsiChar(AudioPaket.data) = STATUS_PACKET) then begin AudioPaket.data := nil; AudioPaketData := nil; @@ -986,7 +988,7 @@ begin Continue; end; - AudioPaketData := PChar(AudioPaket.data); + AudioPaketData := AudioPaket.data; AudioPaketSize := AudioPaket.size; // if available, update the stream position to the presentation time of this package @@ -1005,7 +1007,7 @@ begin end; end; -function TFFmpegDecodeStream.ReadData(Buffer: PChar; BufferSize: integer): integer; +function TFFmpegDecodeStream.ReadData(Buffer: PByteArray; BufferSize: integer): integer; var CopyByteCount: integer; // number of bytes to copy RemainByteCount: integer; // number of bytes left (remain) to read @@ -1116,7 +1118,7 @@ begin Result := true; end; -function TAudioDecoder_FFmpeg.Open(const Filename: string): TAudioDecodeStream; +function TAudioDecoder_FFmpeg.Open(const Filename: IPath): TAudioDecodeStream; var Stream: TFFmpegDecodeStream; begin diff --git a/Lua/src/media/UAudioInput_Bass.pas b/Lua/src/media/UAudioInput_Bass.pas index cf292c45..9d4417f1 100644 --- a/Lua/src/media/UAudioInput_Bass.pas +++ b/Lua/src/media/UAudioInput_Bass.pas @@ -95,7 +95,7 @@ var * user - players associated with left/right channels *} function MicrophoneCallback(stream: HSTREAM; buffer: Pointer; - len: Cardinal; inputDevice: Pointer): boolean; {$IFDEF MSWINDOWS}stdcall;{$ELSE}cdecl;{$ENDIF} + len: integer; inputDevice: Pointer): boolean; {$IFDEF MSWINDOWS}stdcall;{$ELSE}cdecl;{$ENDIF} begin AudioInputProcessor.HandleMicrophoneData(buffer, len, inputDevice); Result := true; @@ -489,6 +489,11 @@ end; function TAudioInput_Bass.InitializeRecord(): boolean; begin BassCore := TAudioCore_Bass.GetInstance(); + if not BassCore.CheckVersion then + begin + Result := false; + Exit; + end; Result := EnumDevices(); end; diff --git a/Lua/src/media/UAudioInput_Portaudio.pas b/Lua/src/media/UAudioInput_Portaudio.pas index 53080a03..31d2882b 100644 --- a/Lua/src/media/UAudioInput_Portaudio.pas +++ b/Lua/src/media/UAudioInput_Portaudio.pas @@ -95,7 +95,6 @@ var Error: TPaError; inputParams: TPaStreamParameters; deviceInfo: PPaDeviceInfo; - SourceIndex: integer; begin Result := false; @@ -291,8 +290,6 @@ var sourceIndex: integer; sourceName: string; {$ENDIF} - cbPolls: integer; - cbWorks: boolean; begin Result := false; diff --git a/Lua/src/media/UAudioPlaybackBase.pas b/Lua/src/media/UAudioPlaybackBase.pas index 7d143fdc..de2d5563 100644 --- a/Lua/src/media/UAudioPlaybackBase.pas +++ b/Lua/src/media/UAudioPlaybackBase.pas @@ -34,7 +34,8 @@ interface {$I switches.inc} uses - UMusic; + UMusic, + UPath; type TAudioPlaybackBase = class(TInterfacedObject, IAudioPlayback) @@ -46,12 +47,12 @@ type function GetLatency(): double; virtual; abstract; // open sound or music stream (used by Open() and OpenSound()) - function OpenStream(const Filename: string): TAudioPlaybackStream; - function OpenDecodeStream(const Filename: string): TAudioDecodeStream; + function OpenStream(const Filename: IPath): TAudioPlaybackStream; + function OpenDecodeStream(const Filename: IPath): TAudioDecodeStream; public function GetName: string; virtual; abstract; - function Open(const Filename: string): boolean; // true if succeed + function Open(const Filename: IPath): boolean; // true if succeed procedure Close; procedure Play; @@ -79,7 +80,7 @@ type function Length: real; // Sounds - function OpenSound(const Filename: string): TAudioPlaybackStream; + function OpenSound(const Filename: IPath): TAudioPlaybackStream; procedure PlaySound(Stream: TAudioPlaybackStream); procedure StopSound(Stream: TAudioPlaybackStream); @@ -108,7 +109,7 @@ begin Result := true; end; -function TAudioPlaybackBase.Open(const Filename: string): boolean; +function TAudioPlaybackBase.Open(const Filename: IPath): boolean; begin // free old MusicStream MusicStream.Free; @@ -130,7 +131,7 @@ begin FreeAndNil(MusicStream); end; -function TAudioPlaybackBase.OpenDecodeStream(const Filename: String): TAudioDecodeStream; +function TAudioPlaybackBase.OpenDecodeStream(const Filename: IPath): TAudioDecodeStream; var i: integer; begin @@ -140,7 +141,7 @@ begin if (assigned(Result)) then begin Log.LogInfo('Using decoder ' + IAudioDecoder(AudioDecoders[i]).GetName() + - ' for "' + Filename + '"', 'TAudioPlaybackBase.OpenDecodeStream'); + ' for "' + Filename.ToNative + '"', 'TAudioPlaybackBase.OpenDecodeStream'); Exit; end; end; @@ -157,7 +158,7 @@ begin SourceStream.Free; end; -function TAudioPlaybackBase.OpenStream(const Filename: string): TAudioPlaybackStream; +function TAudioPlaybackBase.OpenStream(const Filename: IPath): TAudioPlaybackStream; var PlaybackStream: TAudioPlaybackStream; DecodeStream: TAudioDecodeStream; @@ -169,7 +170,7 @@ begin DecodeStream := OpenDecodeStream(Filename); if (not assigned(DecodeStream)) then begin - Log.LogStatus('Could not open "' + Filename + '"', 'TAudioPlayback_Bass.OpenStream'); + Log.LogStatus('Could not open "' + Filename.ToNative + '"', 'TAudioPlayback_Bass.OpenStream'); Exit; end; @@ -283,7 +284,7 @@ begin Result := 0; end; -function TAudioPlaybackBase.OpenSound(const Filename: string): TAudioPlaybackStream; +function TAudioPlaybackBase.OpenSound(const Filename: IPath): TAudioPlaybackStream; begin Result := OpenStream(Filename); end; diff --git a/Lua/src/media/UAudioPlayback_Bass.pas b/Lua/src/media/UAudioPlayback_Bass.pas index d68ac1d4..1d7a44dc 100644 --- a/Lua/src/media/UAudioPlayback_Bass.pas +++ b/Lua/src/media/UAudioPlayback_Bass.pas @@ -37,7 +37,6 @@ implementation uses Classes, - SysUtils, Math, UIni, UMain, @@ -46,7 +45,8 @@ uses UAudioCore_Bass, ULog, sdl, - bass; + bass, + SysUtils; type PHDSP = ^HDSP; @@ -90,7 +90,7 @@ type function GetAudioFormatInfo(): TAudioFormatInfo; override; - function ReadData(Buffer: PChar; BufferSize: integer): integer; + function ReadData(Buffer: PByteArray; BufferSize: integer): integer; property EOF: boolean READ IsEOF; end; @@ -106,8 +106,8 @@ type function Open(ChannelMap: integer; FormatInfo: TAudioFormatInfo): boolean; override; procedure Close(); override; - procedure WriteData(Buffer: PChar; BufferSize: integer); override; - function ReadData(Buffer: PChar; BufferSize: integer): integer; override; + procedure WriteData(Buffer: PByteArray; BufferSize: integer); override; + function ReadData(Buffer: PByteArray; BufferSize: integer): integer; override; function IsEOF(): boolean; override; function IsError(): boolean; override; end; @@ -163,14 +163,14 @@ begin Result := BytesRead; end; -function TBassPlaybackStream.ReadData(Buffer: PChar; BufferSize: integer): integer; +function TBassPlaybackStream.ReadData(Buffer: PByteArray; BufferSize: integer): integer; var AdjustedSize: integer; RequestedSourceSize, SourceSize: integer; SkipCount: integer; SourceFormatInfo: TAudioFormatInfo; FrameSize: integer; - PadFrame: PChar; + PadFrame: PByteArray; //Info: BASS_INFO; //Latency: double; begin @@ -610,7 +610,7 @@ begin inherited Close(); end; -procedure TBassVoiceStream.WriteData(Buffer: PChar; BufferSize: integer); +procedure TBassVoiceStream.WriteData(Buffer: PByteArray; BufferSize: integer); var QueueSize: DWORD; begin if ((Handle <> 0) and (BufferSize > 0)) then @@ -626,7 +626,7 @@ begin end; // Note: we do not need the read-function for the BASS implementation -function TBassVoiceStream.ReadData(Buffer: PChar; BufferSize: integer): integer; +function TBassVoiceStream.ReadData(Buffer: PByteArray; BufferSize: integer): integer; begin Result := -1; end; @@ -684,9 +684,11 @@ end; function TAudioPlayback_Bass.InitializePlayback(): boolean; begin - result := false; + Result := false; BassCore := TAudioCore_Bass.GetInstance(); + if not BassCore.CheckVersion then + Exit; EnumDevices(); @@ -706,7 +708,7 @@ begin //BASS_SetConfig(BASS_CONFIG_UPDATEPERIOD, 10); //BASS_SetConfig(BASS_CONFIG_BUFFER, 100); - result := true; + Result := true; end; function TAudioPlayback_Bass.FinalizePlayback(): boolean; diff --git a/Lua/src/media/UAudioPlayback_SDL.pas b/Lua/src/media/UAudioPlayback_SDL.pas index b0887676..8403ef03 100644 --- a/Lua/src/media/UAudioPlayback_SDL.pas +++ b/Lua/src/media/UAudioPlayback_SDL.pas @@ -33,16 +33,14 @@ interface {$I switches.inc} -uses - Classes, - SysUtils, - UMusic; - implementation uses + Classes, sdl, + SysUtils, UAudioPlayback_SoftMixer, + UMusic, ULog, UIni, UMain; @@ -60,13 +58,13 @@ type function GetLatency(): double; override; public function GetName: String; override; - procedure MixBuffers(dst, src: PChar; size: Cardinal; volume: Single); override; + procedure MixBuffers(dst, src: PByteArray; size: Cardinal; volume: Single); override; end; { TAudioPlayback_SDL } -procedure SDLAudioCallback(userdata: Pointer; stream: PChar; len: integer); cdecl; +procedure SDLAudioCallback(userdata: Pointer; stream: PByteArray; len: integer); cdecl; var Engine: TAudioPlayback_SDL; begin @@ -172,7 +170,7 @@ begin Result := Latency; end; -procedure TAudioPlayback_SDL.MixBuffers(dst, src: PChar; size: Cardinal; volume: Single); +procedure TAudioPlayback_SDL.MixBuffers(dst, src: PByteArray; size: Cardinal; volume: Single); begin SDL_MixAudio(PUInt8(dst), PUInt8(src), size, Round(volume * SDL_MIX_MAXVOLUME)); end; diff --git a/Lua/src/media/UAudioPlayback_SoftMixer.pas b/Lua/src/media/UAudioPlayback_SoftMixer.pas index f3797dd6..c87e461d 100644 --- a/Lua/src/media/UAudioPlayback_SoftMixer.pas +++ b/Lua/src/media/UAudioPlayback_SoftMixer.pas @@ -35,8 +35,8 @@ interface uses Classes, - SysUtils, sdl, + SysUtils, URingBuffer, UMusic, UAudioPlaybackBase; @@ -48,12 +48,12 @@ type private Engine: TAudioPlayback_SoftMixer; - SampleBuffer: PChar; + SampleBuffer: PByteArray; SampleBufferSize: integer; SampleBufferCount: integer; // number of available bytes in SampleBuffer - SampleBufferPos: cardinal; + SampleBufferPos: integer; - SourceBuffer: PChar; + SourceBuffer: PByteArray; SourceBufferSize: integer; SourceBufferCount: integer; // number of available bytes in SourceBuffer @@ -70,7 +70,7 @@ type procedure Reset(); - procedure ApplySoundEffects(Buffer: PChar; BufferSize: integer); + procedure ApplySoundEffects(Buffer: PByteArray; BufferSize: integer); function InitFormatConversion(): boolean; procedure FlushBuffers(); @@ -100,7 +100,7 @@ type function GetAudioFormatInfo(): TAudioFormatInfo; override; - function ReadData(Buffer: PChar; BufferSize: integer): integer; + function ReadData(Buffer: PByteArray; BufferSize: integer): integer; function GetPCMData(var Data: TPCMData): Cardinal; override; procedure GetFFTData(var Data: TFFTData); override; @@ -114,7 +114,7 @@ type Engine: TAudioPlayback_SoftMixer; ActiveStreams: TList; - MixerBuffer: PChar; + MixerBuffer: PByteArray; InternalLock: PSDL_Mutex; AppVolume: single; @@ -129,7 +129,7 @@ type destructor Destroy(); override; procedure AddStream(Stream: TAudioPlaybackStream); procedure RemoveStream(Stream: TAudioPlaybackStream); - function ReadData(Buffer: PChar; BufferSize: integer): integer; + function ReadData(Buffer: PByteArray; BufferSize: integer): integer; property Volume: single read GetVolume write SetVolume; end; @@ -144,7 +144,7 @@ type function StartAudioPlaybackEngine(): boolean; virtual; abstract; procedure StopAudioPlaybackEngine(); virtual; abstract; function FinalizeAudioPlaybackEngine(): boolean; virtual; abstract; - procedure AudioCallback(Buffer: PChar; Size: integer); {$IFDEF HasInline}inline;{$ENDIF} + procedure AudioCallback(Buffer: PByteArray; Size: integer); {$IFDEF HasInline}inline;{$ENDIF} function CreatePlaybackStream(): TAudioPlaybackStream; override; public @@ -159,7 +159,7 @@ type function GetMixer(): TAudioMixerStream; {$IFDEF HasInline}inline;{$ENDIF} function GetAudioFormatInfo(): TAudioFormatInfo; - procedure MixBuffers(DstBuffer, SrcBuffer: PChar; Size: Cardinal; Volume: Single); virtual; + procedure MixBuffers(DstBuffer, SrcBuffer: PByteArray; Size: Cardinal; Volume: Single); virtual; end; type @@ -174,8 +174,8 @@ type function Open(ChannelMap: integer; FormatInfo: TAudioFormatInfo): boolean; override; procedure Close(); override; - procedure WriteData(Buffer: PChar; BufferSize: integer); override; - function ReadData(Buffer: PChar; BufferSize: integer): integer; override; + procedure WriteData(Buffer: PByteArray; BufferSize: integer); override; + function ReadData(Buffer: PByteArray; BufferSize: integer): integer; override; function IsEOF(): boolean; override; function IsError(): boolean; override; end; @@ -276,7 +276,7 @@ begin Unlock(); end; -function TAudioMixerStream.ReadData(Buffer: PChar; BufferSize: integer): integer; +function TAudioMixerStream.ReadData(Buffer: PByteArray; BufferSize: integer): integer; var i: integer; Size: integer; @@ -545,7 +545,7 @@ begin SourceBufferCount := 0; end; -procedure TGenericPlaybackStream.ApplySoundEffects(Buffer: PChar; BufferSize: integer); +procedure TGenericPlaybackStream.ApplySoundEffects(Buffer: PByteArray; BufferSize: integer); var i: integer; begin @@ -558,23 +558,21 @@ begin end; end; -function TGenericPlaybackStream.ReadData(Buffer: PChar; BufferSize: integer): integer; +function TGenericPlaybackStream.ReadData(Buffer: PByteArray; BufferSize: integer): integer; var ConversionInputCount: integer; ConversionOutputSize: integer; // max. number of converted data (= buffer size) ConversionOutputCount: integer; // actual number of converted data SourceSize: integer; - RequestedSourceSize: integer; NeededSampleBufferSize: integer; - BytesNeeded, BytesAvail: integer; + BytesNeeded: integer; SourceFormatInfo, OutputFormatInfo: TAudioFormatInfo; SourceFrameSize, OutputFrameSize: integer; SkipOutputCount: integer; // number of output-data bytes to skip SkipSourceCount: integer; // number of source-data bytes to skip FillCount: integer; // number of bytes to fill with padding data CopyCount: integer; - PadFrame: PChar; - i: integer; + PadFrame: PByteArray; begin Result := -1; @@ -986,7 +984,7 @@ begin inherited Close(); end; -procedure TGenericVoiceStream.WriteData(Buffer: PChar; BufferSize: integer); +procedure TGenericVoiceStream.WriteData(Buffer: PByteArray; BufferSize: integer); begin // lock access to buffer SDL_mutexP(BufferLock); @@ -999,7 +997,7 @@ begin end; end; -function TGenericVoiceStream.ReadData(Buffer: PChar; BufferSize: integer): integer; +function TGenericVoiceStream.ReadData(Buffer: PByteArray; BufferSize: integer): integer; begin Result := -1; @@ -1059,7 +1057,7 @@ begin Result := true; end; -procedure TAudioPlayback_SoftMixer.AudioCallback(Buffer: PChar; Size: integer); +procedure TAudioPlayback_SoftMixer.AudioCallback(Buffer: PByteArray; Size: integer); begin MixerStream.ReadData(Buffer, Size); end; @@ -1102,7 +1100,7 @@ begin MixerStream.Volume := Volume; end; -procedure TAudioPlayback_SoftMixer.MixBuffers(DstBuffer, SrcBuffer: PChar; Size: Cardinal; Volume: Single); +procedure TAudioPlayback_SoftMixer.MixBuffers(DstBuffer, SrcBuffer: PByteArray; Size: Cardinal; Volume: Single); var SampleIndex: Cardinal; SampleInt: Integer; diff --git a/Lua/src/media/UMediaCore_FFmpeg.pas b/Lua/src/media/UMediaCore_FFmpeg.pas index 9ad19a5b..b4951fe1 100644 --- a/Lua/src/media/UMediaCore_FFmpeg.pas +++ b/Lua/src/media/UMediaCore_FFmpeg.pas @@ -34,12 +34,16 @@ interface {$I switches.inc} uses - UMusic, + Classes, + ctypes, + sdl, avcodec, avformat, avutil, + avio, + UMusic, ULog, - sdl; + UPath; type PPacketQueue = ^TPacketQueue; @@ -97,12 +101,29 @@ implementation uses SysUtils; +function FFmpegStreamOpen(h: PURLContext; filename: PChar; flags: cint): cint; cdecl; forward; +function FFmpegStreamRead(h: PURLContext; buf: PByteArray; size: cint): cint; cdecl; forward; +function FFmpegStreamWrite(h: PURLContext; buf: PByteArray; size: cint): cint; cdecl; forward; +function FFmpegStreamSeek(h: PURLContext; pos: int64; whence: cint): int64; cdecl; forward; +function FFmpegStreamClose(h: PURLContext): cint; cdecl; forward; + +const + UTF8FileProtocol: TURLProtocol = ( + name: 'ufile'; + url_open: FFmpegStreamOpen; + url_read: FFmpegStreamRead; + url_write: FFmpegStreamWrite; + url_seek: FFmpegStreamSeek; + url_close: FFmpegStreamClose; + ); + var Instance: TMediaCore_FFmpeg; constructor TMediaCore_FFmpeg.Create(); begin inherited; + av_register_protocol(@UTF8FileProtocol); AVCodecLock := SDL_CreateMutex(); end; @@ -220,6 +241,105 @@ begin Result := true; end; + +{** + * UTF-8 Filename wrapper based on: + * http://www.mail-archive.com/libav-user@mplayerhq.hu/msg02460.html + *} + +function FFmpegStreamOpen(h: PURLContext; filename: PChar; flags: cint): cint; cdecl; +var + Stream: TStream; + Mode: word; + ProtPrefix: string; + FilePath: IPath; +begin + // check for protocol prefix ('ufile:') and strip it + ProtPrefix := Format('%s:', [UTF8FileProtocol.name]); + if (StrLComp(filename, PChar(ProtPrefix), Length(ProtPrefix)) = 0) then + begin + Inc(filename, Length(ProtPrefix)); + end; + + FilePath := Path(filename); + + if ((flags and URL_RDWR) <> 0) then + Mode := fmCreate + else if ((flags and URL_WRONLY) <> 0) then + Mode := fmCreate // TODO: fmCreate is Read+Write -> reopen with fmOpenWrite + else + Mode := fmOpenRead; + + Result := 0; + + try + Stream := TBinaryFileStream.Create(FilePath, Mode); + h.priv_data := Stream; + except + Result := AVERROR_NOENT; + end; +end; + +function FFmpegStreamRead(h: PURLContext; buf: PByteArray; size: cint): cint; cdecl; +var + Stream: TStream; +begin + Stream := TStream(h.priv_data); + if (Stream = nil) then + raise EInvalidContainer.Create('FFmpegStreamRead on nil'); + try + Result := Stream.Read(buf[0], size); + except + Result := -1; + end; +end; + +function FFmpegStreamWrite(h: PURLContext; buf: PByteArray; size: cint): cint; cdecl; +var + Stream: TStream; +begin + Stream := TStream(h.priv_data); + if (Stream = nil) then + raise EInvalidContainer.Create('FFmpegStreamWrite on nil'); + try + Result := Stream.Write(buf[0], size); + except + Result := -1; + end; +end; + +function FFmpegStreamSeek(h: PURLContext; pos: int64; whence: cint): int64; cdecl; +var + Stream : TStream; + Origin : TSeekOrigin; +begin + Stream := TStream(h.priv_data); + if (Stream = nil) then + raise EInvalidContainer.Create('FFmpegStreamSeek on nil'); + case whence of + 0 {SEEK_SET}: Origin := soBeginning; + 1 {SEEK_CUR}: Origin := soCurrent; + 2 {SEEK_END}: Origin := soEnd; + AVSEEK_SIZE: begin + Result := Stream.Size; + Exit; + end + else + Origin := soBeginning; + end; + Result := Stream.Seek(pos, Origin); +end; + +function FFmpegStreamClose(h: PURLContext): cint; cdecl; +var + Stream : TStream; +begin + Stream := TStream(h.priv_data); + Stream.Free; + Result := 0; +end; + + { TPacketQueue } constructor TPacketQueue.Create(); diff --git a/Lua/src/media/UMedia_dummy.pas b/Lua/src/media/UMedia_dummy.pas index 7558dd0b..25e94724 100644 --- a/Lua/src/media/UMedia_dummy.pas +++ b/Lua/src/media/UMedia_dummy.pas @@ -36,9 +36,10 @@ interface implementation uses - SysUtils, - math, - UMusic; + SysUtils, + math, + UMusic, + UPath; type TMedia_dummy = class( TInterfacedObject, IVideoPlayback, IVideoVisualization, IAudioPlayback, IAudioInput ) @@ -51,7 +52,7 @@ type function Init(): boolean; function Finalize(): boolean; - function Open(const aFileName : string): boolean; // true if succeed + function Open(const aFileName: IPath): boolean; // true if succeed procedure Close; procedure Play; @@ -88,7 +89,7 @@ type function Finished: boolean; function Length: real; - function OpenSound(const Filename: string): TAudioPlaybackStream; + function OpenSound(const Filename: IPath): TAudioPlaybackStream; procedure CloseSound(var PlaybackStream: TAudioPlaybackStream); procedure PlaySound(stream: TAudioPlaybackStream); procedure StopSound(stream: TAudioPlaybackStream); @@ -125,7 +126,7 @@ begin Result := true; end; -function TMedia_dummy.Open(const aFileName : string): boolean; // true if succeed +function TMedia_dummy.Open(const aFileName : IPath): boolean; // true if succeed begin Result := false; end; @@ -236,7 +237,7 @@ begin Result := 60; end; -function TMedia_dummy.OpenSound(const Filename: string): TAudioPlaybackStream; +function TMedia_dummy.OpenSound(const Filename: IPath): TAudioPlaybackStream; begin Result := nil; end; diff --git a/Lua/src/media/UVideo.pas b/Lua/src/media/UVideo.pas index 197fc572..6db9cd20 100644 --- a/Lua/src/media/UVideo.pas +++ b/Lua/src/media/UVideo.pas @@ -22,7 +22,7 @@ * $URL$ * $Id$ *} - + unit UVideo; {* @@ -69,8 +69,9 @@ type implementation uses + SysUtils, + Math, SDL, - textgl, avcodec, avformat, avutil, @@ -79,17 +80,17 @@ uses {$IFDEF UseSWScale} swscale, {$ENDIF} - UMediaCore_FFmpeg, - math, gl, glext, - SysUtils, + textgl, + UMediaCore_FFmpeg, UCommon, UConfig, ULog, UMusic, UGraphicClasses, - UGraphic; + UGraphic, + UPath; const {$IFDEF PIXEL_FMT_BGR} @@ -135,7 +136,7 @@ type fAspect: real; //**< width/height ratio fAspectCorrection: TAspectCorrection; - + fTimeBase: extended; //**< FFmpeg time base per time unit fTime: extended; //**< video time position (absolute) fLoopTime: extended; //**< start time of the current loop @@ -145,7 +146,7 @@ type procedure SynchronizeTime(Frame: PAVFrame; var pts: double); procedure GetVideoRect(var ScreenRect, TexRect: TRectCoords); - + procedure ShowDebugInfo(); public @@ -154,7 +155,7 @@ type function Init(): boolean; function Finalize: boolean; - function Open(const aFileName : string): boolean; // true if succeed + function Open(const FileName : IPath): boolean; // true if succeed procedure Close; procedure Play; @@ -171,7 +172,7 @@ type var FFmpegCore: TMediaCore_FFmpeg; - + // These are called whenever we allocate a frame buffer. // We use this to store the global_pts in a frame at the time it is allocated. function PtsGetBuffer(CodecCtx: PAVCodecContext; Frame: PAVFrame): integer; cdecl; @@ -248,11 +249,11 @@ begin // TODO: do we really want this by default? fLoop := true; fLoopTime := 0; - + fAspectCorrection := acoCrop; end; -function TVideoPlayback_FFmpeg.Open(const aFileName : string): boolean; // true if succeed +function TVideoPlayback_FFmpeg.Open(const FileName : IPath): boolean; // true if succeed var errnum: Integer; AudioStreamIndex: integer; @@ -261,10 +262,11 @@ begin Reset(); - errnum := av_open_input_file(fFormatContext, PChar(aFileName), nil, 0, nil); + // use custom 'ufile' protocol for UTF-8 support + errnum := av_open_input_file(fFormatContext, PAnsiChar('ufile:'+FileName.ToNative), nil, 0, nil); if (errnum <> 0) then begin - Log.LogError('Failed to open file "'+aFileName+'" ('+FFmpegCore.GetErrorString(errnum)+')'); + Log.LogError('Failed to open file "'+ FileName.ToNative +'" ('+FFmpegCore.GetErrorString(errnum)+')'); Exit; end; @@ -434,7 +436,7 @@ begin fAVFrame := nil; fAVFrameRGB := nil; fFrameBuffer := nil; - + if (fCodecContext <> nil) then begin // avcodec_close() is not thread-safe @@ -479,7 +481,7 @@ end; * Decode a new frame from the video stream. * The decoded frame is stored in fAVFrame. fTime is updated to the new frame's * time. - * @param pts will be updated to the presentation time of the decoded frame. + * @param pts will be updated to the presentation time of the decoded frame. * returns true if a frame could be decoded. False if an error or EOF occured. *} function TVideoPlayback_FFmpeg.DecodeFrame(): boolean; @@ -532,8 +534,17 @@ begin end; // no error -> wait for user input - SDL_Delay(100); +{ + SDL_Delay(100); // initial version, left for documentation continue; +} + + // Patch by Hawkear: + // Why should this function loop in an endless loop if there is an error? + // This runs in the main thread, so it halts the whole program + // Therefore, it is better to exit when an error occurs + Exit; + end; // if we got a packet from the video stream, then decode it @@ -564,6 +575,10 @@ begin begin pts := 0; end; + + if fStream^.start_time <> AV_NOPTS_VALUE then + pts := pts - fStream^.start_time; + pts := pts * av_q2d(fStream^.time_base); // synchronize time on each complete frame @@ -611,7 +626,7 @@ begin 'TimeDiff: '+inttostr(floor(TimeDifference*1000))); {$endif} - // check if last time is more than one frame in the past + // check if last time is more than one frame in the past if (TimeDifference < fTimeBase) then begin {$ifdef DebugFrames} @@ -635,7 +650,7 @@ begin {$IFDEF VideoBenchmark} Log.BenchmarkStart(15); {$ENDIF} - + // fetch new frame (updates fTime) Success := DecodeFrame(); TimeDifference := NewTime - fTime; @@ -662,7 +677,7 @@ begin Success := DecodeFrame(); end; - // check if we got an EOF or error + // check if we got an EOF or error if (not Success) then begin if fLoop then @@ -688,11 +703,17 @@ begin 0, fCodecContext^.Height, @(fAVFrameRGB.data), @(fAVFrameRGB.linesize)); {$ELSE} + // img_convert from lib/ffmpeg/avcodec.pas is actually deprecated. + // If ./configure does not find SWScale then this gives the error + // that the identifier img_convert is not known or similar. + // I think this should be removed, but am not sure whether there should + // be some other replacement or a warning, Therefore, I leave it for now. + // April 2009, mischi errnum := img_convert(PAVPicture(fAVFrameRGB), PIXEL_FMT_FFMPEG, PAVPicture(fAVFrame), fCodecContext^.pix_fmt, fCodecContext^.width, fCodecContext^.height); {$ENDIF} - + if (errnum < 0) then begin Log.LogError('Image conversion failed', 'TVideoPlayback_ffmpeg.GetFrame'); @@ -785,8 +806,18 @@ var begin // have a nice black background to draw on // (even if there were errors opening the vid) - glClearColor(0, 0, 0, 0); - glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT); + // TODO: Philipp: IMO TVideoPlayback should not clear the screen at + // all, because clearing is already done by the background class + // at this moment. + if (Screen = 1) then + begin + // It is important that we just clear once before we start + // drawing the first screen otherwise the first screen + // would be cleared by the drawgl called when the second + // screen is drawn + glClearColor(0, 0, 0, 0); + glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT); + end; // exit if there's nothing to draw if (not fOpened) then @@ -913,7 +944,7 @@ begin fTime := Time; fEOF := false; - fFrameTexValid := false; + fFrameTexValid := false; if (av_seek_frame(fFormatContext, fStreamIndex, Floor(Time/fTimeBase), SeekFlags) < 0) then begin diff --git a/Lua/src/media/UVisualizer.pas b/Lua/src/media/UVisualizer.pas index 9af6d8c2..b25d68a9 100644 --- a/Lua/src/media/UVisualizer.pas +++ b/Lua/src/media/UVisualizer.pas @@ -77,6 +77,7 @@ uses UGraphic, UMain, UConfig, + UPath, ULog; {$IF PROJECTM_VERSION < 1000000} // < 1.0 @@ -130,7 +131,7 @@ type function Init(): boolean; function Finalize(): boolean; - function Open(const aFileName : string): boolean; // true if succeed + function Open(const aFileName: IPath): boolean; // true if succeed procedure Close; procedure Play; @@ -183,7 +184,7 @@ begin Result := true; end; -function TVideoPlayback_ProjectM.Open(const aFileName : string): boolean; // true if succeed +function TVideoPlayback_ProjectM.Open(const aFileName: IPath): boolean; // true if succeed begin Result := false; end; @@ -484,7 +485,11 @@ begin glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); - gluOrtho2D(0, 1, 0, 1); + // Use count of screens instead of 1 for the right corner + // otherwise we would draw the visualization streched over both screens + // another point is that we draw over the at this time drawn first + // screen, if Screen = 2 + gluOrtho2D(0, Screens, 0, 1); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); @@ -496,11 +501,12 @@ begin glColor4f(1, 1, 1, 1); // draw projectM frame + // Screen is 1 to 2. So current screen is from (Screen - 1) to (Screen) glBegin(GL_QUADS); - glTexCoord2f(0, 0); glVertex2f(0, 0); - glTexCoord2f(1, 0); glVertex2f(1, 0); - glTexCoord2f(1, 1); glVertex2f(1, 1); - glTexCoord2f(0, 1); glVertex2f(0, 1); + glTexCoord2f(0, 0); glVertex2f((Screen - 1), 0); + glTexCoord2f(1, 0); glVertex2f(Screen, 0); + glTexCoord2f(1, 1); glVertex2f(Screen, 1); + glTexCoord2f(0, 1); glVertex2f((Screen - 1), 1); glEnd(); glDisable(GL_TEXTURE_2D); diff --git a/Lua/src/menu/UDisplay.pas b/Lua/src/menu/UDisplay.pas index 525b73a9..f8f9c43f 100644 --- a/Lua/src/menu/UDisplay.pas +++ b/Lua/src/menu/UDisplay.pas @@ -34,78 +34,116 @@ interface {$I switches.inc} uses - ucommon, + UCommon, SDL, - UMenu, gl, glu, SysUtils, + UMenu, + UPath, UMusic, UHookableEvent; type TDisplay = class private + ePreDraw: THookableEvent; + eDraw: THookableEvent; + //fade-to-black-hack - BlackScreen: Boolean; + BlackScreen: boolean; - FadeEnabled: Boolean; // true if fading is enabled - FadeFailed: Boolean; // true if fading is possible (enough memory, etc.) - FadeState: integer; // fading state, 0 means that the fade texture must be initialized - LastFadeTime: Cardinal; // last fade update time + FadeEnabled: boolean; // true if fading is enabled + FadeFailed: boolean; // true if fading is possible (enough memory, etc.) + FadeState: integer; // fading state, 0 means that the fade texture must be initialized + LastFadeTime: cardinal; // last fade update time - FadeTex: array[1..2] of GLuint; + FadeTex: array[1..2] of GLuint; + + FPSCounter: cardinal; + LastFPS: cardinal; + NextFPSSwap: cardinal; - FPSCounter : Cardinal; - LastFPS : Cardinal; - NextFPSSwap : Cardinal; + OSD_LastError: string; - OSD_LastError : String; + { software cursor data } + Cursor_X: double; + Cursor_Y: double; + Cursor_Pressed: boolean; + Cursor_HiddenByScreen: boolean; // hides software cursor and deactivate auto fade in - ePreDraw: THookableEvent; - eDraw: THookableEvent; + // used for cursor fade out when there is no movement + Cursor_Visible: boolean; + Cursor_LastMove: cardinal; + Cursor_Fade: boolean; procedure DrawDebugInformation; + + { called by MoveCursor and OnMouseButton to update last move and start fade in } + procedure UpdateCursorFade; public - NextScreen : PMenu; - CurrentScreen : PMenu; + NextScreen: PMenu; + CurrentScreen: PMenu; //popup data NextScreenWithCheck: Pmenu; - CheckOK : Boolean; + CheckOK: boolean; // FIXME: Fade is set to 0 in UMain and other files but not used here anymore. - Fade : Real; + Fade: real; constructor Create; destructor Destroy; override; procedure SaveScreenShot; + function Draw: boolean; + + { calls ParseInput of cur or next Screen if assigned } + function ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown : boolean): boolean; + + { sets SDL_ShowCursor depending on options set in Ini } + procedure SetCursor; + + { called when cursor moves, positioning of software cursor } + procedure MoveCursor(X, Y: double); + + { called when left or right mousebutton is pressed or released } + procedure OnMouseButton(Pressed: boolean); { fades to specific screen (playing specified sound) } function FadeTo(Screen: PMenu; const aSound: TAudioPlaybackStream = nil): PMenu; { abort fading to the current screen, may be used in OnShow, or during fade process } procedure AbortScreenChange; - function Draw: Boolean; + { draws software cursor } + procedure DrawCursor; end; var - Display: TDisplay; + Display: TDisplay; + +const + { constants for software cursor effects + time in milliseconds } + Cursor_FadeIn_Time = 500; // seconds the fade in effect lasts + Cursor_FadeOut_Time = 2000; // seconds the fade out effect lasts + Cursor_AutoHide_Time = 5000; // seconds until auto fade out starts if there is no mouse movement implementation uses - UImage, TextGL, + UCommandLine, + UGraphic, + UIni, + UImage, ULog, UMain, UTexture, - UIni, - UGraphic, UTime, - UCommandLine; + ULanguage, + UPathUtils; constructor TDisplay.Create; var @@ -113,16 +151,20 @@ var begin inherited Create; + // create events for plugins + ePreDraw := THookableEvent.Create('Display.PreDraw'); + eDraw := THookableEvent.Create('Display.Draw'); + //popup hack - CheckOK := False; + CheckOK := false; NextScreen := nil; NextScreenWithCheck := nil; - BlackScreen := False; + BlackScreen := false; // fade mod - FadeState := 0; + FadeState := 0; FadeEnabled := (Ini.ScreenFade = 1); - FadeFailed:= false; + FadeFailed := false; glGenTextures(2, @FadeTex); @@ -136,9 +178,14 @@ begin //Set LastError for OSD to No Error OSD_LastError := 'No Errors'; - // create events for plugins - ePreDraw := THookableEvent.Create('Display.PreDraw'); - eDraw := THookableEvent.Create('Display.Draw'); + // software cursor default values + Cursor_LastMove := 0; + Cursor_Visible := false; + Cursor_Pressed := false; + Cursor_X := -1; + Cursor_Y := -1; + Cursor_Fade := false; + Cursor_HiddenByScreen := true; end; destructor TDisplay.Destroy; @@ -147,14 +194,14 @@ begin inherited Destroy; end; -function TDisplay.Draw: Boolean; +function TDisplay.Draw: boolean; var - S: integer; - FadeStateSquare: Real; - currentTime: Cardinal; - glError: glEnum; + S: integer; + FadeStateSquare: real; + currentTime: cardinal; + glError: glEnum; begin - Result := True; + Result := true; //We don't need this here anymore, //Because the background care about cleaning the buffers @@ -180,12 +227,12 @@ begin begin NextScreen := NextScreenWithCheck; NextScreenWithCheck := nil; - CheckOk := False; + CheckOk := false; end else begin // on end of game fade to black before exit - BlackScreen := True; + BlackScreen := true; end; end; @@ -197,15 +244,17 @@ begin //popup mod if (ScreenPopupError <> nil) and ScreenPopupError.Visible then ScreenPopupError.Draw + else if (ScreenPopupInfo <> nil) and ScreenPopupInfo.Visible then + ScreenPopupInfo.Draw else if (ScreenPopupCheck <> nil) and ScreenPopupCheck.Visible then ScreenPopupCheck.Draw; // fade mod FadeState := 0; if ((Ini.ScreenFade = 1) and (not FadeFailed)) then - FadeEnabled := True + FadeEnabled := true else if (Ini.ScreenFade = 0) then - FadeEnabled := False; + FadeEnabled := false; eDraw.CallHookChain(false); end @@ -214,7 +263,7 @@ begin // disable fading if initialization failed if (FadeEnabled and FadeFailed) then begin - FadeEnabled := False; + FadeEnabled := false; end; if (FadeEnabled and not FadeFailed) then @@ -251,7 +300,7 @@ begin // blackscreen-hack if not BlackScreen then - NextScreen.onShow; + NextScreen.OnShow; // update fade state LastFadeTime := SDL_GetTicks(); @@ -299,7 +348,7 @@ begin glDisable(GL_BLEND); glDisable(GL_TEXTURE_2D); end - // blackscreen hack +// blackscreen hack else if not BlackScreen then begin NextScreen.OnShow; @@ -310,26 +359,208 @@ begin // fade out complete... FadeState := 0; CurrentScreen.onHide; - CurrentScreen.ShowFinish := False; + CurrentScreen.ShowFinish := false; CurrentScreen := NextScreen; NextScreen := nil; if not BlackScreen then begin - CurrentScreen.onShowFinish; + CurrentScreen.OnShowFinish; CurrentScreen.ShowFinish := true; end else begin - Result := False; + Result := false; Break; end; end; end; // if - //Draw OSD only on first Screen if Debug Mode is enabled +// Draw OSD only on first Screen if Debug Mode is enabled if ((Ini.Debug = 1) or (Params.Debug)) and (S = 1) then DrawDebugInformation; end; // for + + if not BlackScreen then + DrawCursor; +end; + +{ sets SDL_ShowCursor depending on options set in Ini } +procedure TDisplay.SetCursor; +var + Cursor: Integer; +begin + Cursor := 0; + + if (CurrentScreen <> @ScreenSing) or (Cursor_HiddenByScreen) then + begin // hide cursor on singscreen + if (Ini.Mouse = 0) and (Ini.FullScreen = 0) then + // show sdl (os) cursor in window mode even when mouse support is off + Cursor := 1 + else if (Ini.Mouse = 1) then + // show sdl (os) cursor when hardware cursor is selected + Cursor := 1; + + if (Ini.Mouse <> 2) then + Cursor_HiddenByScreen := false; + end + else if (Ini.Mouse <> 2) then + Cursor_HiddenByScreen := true; + + + SDL_ShowCursor(Cursor); + + if (Ini.Mouse = 2) then + begin + if Cursor_HiddenByScreen then + begin + // show software cursor + Cursor_HiddenByScreen := false; + Cursor_Visible := false; + Cursor_Fade := false; + end + else if (CurrentScreen = @ScreenSing) then + begin + // hide software cursor in singscreen + Cursor_HiddenByScreen := true; + Cursor_Visible := false; + Cursor_Fade := false; + end; + end; +end; + +{ called by MoveCursor and OnMouseButton to update last move and start fade in } +procedure TDisplay.UpdateCursorFade; +var + Ticks: cardinal; +begin + Ticks := SDL_GetTicks; + + { fade in on movement (or button press) if not first movement } + if (not Cursor_Visible) and (Cursor_LastMove <> 0) then + begin + if Cursor_Fade then // we use a trick here to consider progress of fade out + Cursor_LastMove := Ticks - round(Cursor_FadeIn_Time * (1 - (Ticks - Cursor_LastMove)/Cursor_FadeOut_Time)) + else + Cursor_LastMove := Ticks; + + Cursor_Visible := true; + Cursor_Fade := true; + end + else if not Cursor_Fade then + begin + Cursor_LastMove := Ticks; + end; +end; + +{ called when cursor moves, positioning of software cursor } +procedure TDisplay.MoveCursor(X, Y: double); +begin + if (Ini.Mouse = 2) and + ((X <> Cursor_X) or (Y <> Cursor_Y)) then + begin + Cursor_X := X; + Cursor_Y := Y; + + UpdateCursorFade; + end; +end; + +{ called when left or right mousebutton is pressed or released } +procedure TDisplay.OnMouseButton(Pressed: boolean); +begin + if (Ini.Mouse = 2) then + begin + Cursor_Pressed := Pressed; + + UpdateCursorFade; + end; +end; + +{ draws software cursor } +procedure TDisplay.DrawCursor; +var + Alpha: single; + Ticks: cardinal; +begin + if (Ini.Mouse = 2) then + begin // draw software cursor + Ticks := SDL_GetTicks; + + if (Cursor_Visible) and (Cursor_LastMove + Cursor_AutoHide_Time <= Ticks) then + begin // start fade out after 5 secs w/o activity + Cursor_Visible := false; + Cursor_LastMove := Ticks; + Cursor_Fade := true; + end; + + // fading + if Cursor_Fade then + begin + if Cursor_Visible then + begin // fade in + if (Cursor_LastMove + Cursor_FadeIn_Time <= Ticks) then + Cursor_Fade := false + else + Alpha := sin((Ticks - Cursor_LastMove) * 0.5 * pi / Cursor_FadeIn_Time) * 0.7; + end + else + begin //fade out + if (Cursor_LastMove + Cursor_FadeOut_Time <= Ticks) then + Cursor_Fade := false + else + Alpha := cos((Ticks - Cursor_LastMove) * 0.5 * pi / Cursor_FadeOut_Time) * 0.7; + end; + end; + + // no else if here because we may turn off fade in if block + if not Cursor_Fade then + begin + if Cursor_Visible then + Alpha := 0.7 // alpha when cursor visible and not fading + else + Alpha := 0; // alpha when cursor is hidden + end; + + if (Alpha > 0) and (not Cursor_HiddenByScreen) then + begin + glColor4f(1, 1, 1, Alpha); + glEnable(GL_TEXTURE_2D); + glEnable(GL_BLEND); + glDisable(GL_DEPTH_TEST); + + if (Cursor_Pressed) and (Tex_Cursor_Pressed.TexNum > 0) then + glBindTexture(GL_TEXTURE_2D, Tex_Cursor_Pressed.TexNum) + else + glBindTexture(GL_TEXTURE_2D, Tex_Cursor_Unpressed.TexNum); + + glBegin(GL_QUADS); + glTexCoord2f(0, 0); + glVertex2f(Cursor_X, Cursor_Y); + + glTexCoord2f(0, 1); + glVertex2f(Cursor_X, Cursor_Y + 32); + + glTexCoord2f(1, 1); + glVertex2f(Cursor_X + 32, Cursor_Y + 32); + + glTexCoord2f(1, 0); + glVertex2f(Cursor_X + 32, Cursor_Y); + glEnd; + + glDisable(GL_BLEND); + glDisable(GL_TEXTURE_2D); + end; + end; +end; + +function TDisplay.ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown : boolean): boolean; +begin + if (assigned(NextScreen)) then + Result := NextScreen^.ParseInput(PressedKey, CharCode, PressedDown) + else if (assigned(CurrentScreen)) then + Result := CurrentScreen^.ParseInput(PressedKey, CharCode, PressedDown) + else + Result := True; end; { abort fading to the next screen, may be used in OnShow, or during fade process } @@ -374,7 +605,8 @@ end; procedure TDisplay.SaveScreenShot; var Num: integer; - FileName: string; + FileName: IPath; + Prefix: UTF8String; ScreenData: PChar; Surface: PSDL_Surface; Success: boolean; @@ -382,17 +614,16 @@ var RowSize: integer; begin // Exit if Screenshot-path does not exist or read-only - if (ScreenshotsPath = '') then + if (ScreenshotsPath.IsUnset) then Exit; for Num := 1 to 9999 do begin - FileName := IntToStr(Num); - while Length(FileName) < 4 do - FileName := '0' + FileName; - FileName := ScreenshotsPath + 'screenshot' + FileName + '.png'; - if not FileExists(FileName) then - break + // fill prefix to 4 digits with leading '0', e.g. '0001' + Prefix := Format('screenshot%.4d', [Num]); + FileName := ScreenshotsPath.Append(Prefix + '.png'); + if not FileName.Exists() then + break; end; // we must take the row-alignment (4byte by default) into account @@ -402,33 +633,34 @@ begin GetMem(ScreenData, RowSize * ScreenH); glReadPixels(0, 0, ScreenW, ScreenH, GL_RGB, GL_UNSIGNED_BYTE, ScreenData); -// on big endian machines (powerpc) this may need to be changed to -// Needs to be tests. KaMiSchi Sept 2008 -// in this case one may have to add " glext, " to the list of used units -// glReadPixels(0, 0, ScreenW, ScreenH, GL_BGR, GL_UNSIGNED_BYTE, ScreenData); + // on big endian machines (powerpc) this may need to be changed to + // Needs to be tests. KaMiSchi Sept 2008 + // in this case one may have to add " glext, " to the list of used units + // glReadPixels(0, 0, ScreenW, ScreenH, GL_BGR, GL_UNSIGNED_BYTE, ScreenData); Surface := SDL_CreateRGBSurfaceFrom( ScreenData, ScreenW, ScreenH, 24, RowSize, $0000FF, $00FF00, $FF0000, 0); - //Success := WriteJPGImage(FileName, Surface, 95); - //Success := WriteBMPImage(FileName, Surface); + // Success := WriteJPGImage(FileName, Surface, 95); + // Success := WriteBMPImage(FileName, Surface); Success := WritePNGImage(FileName, Surface); if Success then - ScreenPopupError.ShowPopup('Screenshot saved: ' + ExtractFileName(FileName)) + ScreenPopupInfo.ShowPopup(Format(Language.Translate('SCREENSHOT_SAVED'), [FileName.GetName.ToUTF8()])) else - ScreenPopupError.ShowPopup('Screenshot failed'); + ScreenPopupError.ShowPopup(Language.Translate('SCREENSHOT_FAILED')); SDL_FreeSurface(Surface); FreeMem(ScreenData); end; //------------ -// DrawDebugInformation - Procedure draw FPS and some other Informations on Screen +// DrawDebugInformation - procedure draw fps and some other informations on screen //------------ procedure TDisplay.DrawDebugInformation; -var Ticks: Cardinal; +var + Ticks: cardinal; begin - //Some White Background for information + // Some White Background for information glEnable(GL_BLEND); glDisable(GL_TEXTURE_2D); glColor4f(1, 1, 1, 0.5); @@ -440,13 +672,13 @@ begin glEnd; glDisable(GL_BLEND); - //Set Font Specs + // set font specs SetFontStyle(0); SetFontSize(21); - SetFontItalic(False); + SetFontItalic(false); glColor4f(0, 0, 0, 1); - //Calculate FPS + // calculate fps Ticks := SDL_GetTicks(); if (Ticks >= NextFPSSwap) then begin @@ -457,17 +689,17 @@ begin Inc(FPSCounter); - //Draw Text + // draw text - //FPS + // fps SetFontPos(695, 0); glPrint ('FPS: ' + InttoStr(LastFPS)); - //RSpeed + // rspeed SetFontPos(695, 13); glPrint ('RSpeed: ' + InttoStr(Round(1000 * TimeMid))); - //LastError + // lasterror SetFontPos(695, 26); glColor4f(1, 0, 0, 1); glPrint (OSD_LastError); diff --git a/Lua/src/menu/UMenu.pas b/Lua/src/menu/UMenu.pas index 16ecc658..3ac487de 100644 --- a/Lua/src/menu/UMenu.pas +++ b/Lua/src/menu/UMenu.pas @@ -34,19 +34,21 @@ interface {$I switches.inc} uses - gl, SysUtils, - UTexture, - UMenuStatic, - UMenuText, - UMenuButton, - UMenuSelectSlide, - UMenuInteract, + Math, + gl, + SDL, + UPath, UMenuBackground, - UThemes, + UMenuButton, UMenuButtonCollection, - Math, - UMusic; + UMenuInteract, + UMenuSelectSlide, + UMenuStatic, + UMenuText, + UMusic, + UTexture, + UThemes; type { Int16 = SmallInt;} @@ -54,15 +56,15 @@ type PMenu = ^TMenu; TMenu = class protected - Background: TMenuBackground; + Background: TMenuBackground; - Interactions: array of TInteract; - SelInteraction: integer; + Interactions: array of TInteract; + SelInteraction: integer; - ButtonPos: integer; - Button: array of TButton; - - SelectsS: array of TSelectSlide; + ButtonPos: integer; + Button: array of TButton; + + SelectsS: array of TSelectSlide; ButtonCollection: array of TButtonCollection; public Text: array of TText; @@ -72,6 +74,7 @@ type Fade: integer; // fade type ShowFinish: boolean; // true if there is no fade + RightMbESC: boolean; // true to simulate ESC keypress when RMB is pressed destructor Destroy; override; constructor Create; overload; virtual; @@ -79,13 +82,11 @@ type //constructor Create(Back: string; W, H: integer); overload; virtual; // W and H are the number of overlaps // interaction - function WideCharUpperCase(wchar: WideChar) : WideString; - function WideStringUpperCase(wstring: WideString) : WideString; procedure AddInteraction(Typ, Num: integer); - procedure SetInteraction(Num: integer); + procedure SetInteraction(Num: integer); virtual; property Interaction: integer read SelInteraction write SetInteraction; - //Procedure Load BG, Texts, Statics and Button Collections from ThemeBasic + // procedure load bg, texts, statics and button collections from themebasic procedure LoadFromTheme(const ThemeBasic: TThemeBasic); procedure PrepareButtonCollections(const Collections: AThemeButtonCollection); @@ -96,61 +97,62 @@ type // static function AddStatic(ThemeStatic: TThemeStatic): integer; overload; - function AddStatic(X, Y, W, H: real; const Name: string): integer; overload; - function AddStatic(X, Y, W, H: real; const Name: string; Typ: TTextureType): integer; overload; - function AddStatic(X, Y, W, H: real; ColR, ColG, ColB: real; const Name: string; Typ: TTextureType): integer; overload; - function AddStatic(X, Y, W, H, Z: real; ColR, ColG, ColB: real; const Name: string; Typ: TTextureType): integer; overload; - function AddStatic(X, Y, W, H: real; ColR, ColG, ColB: real; const Name: string; Typ: TTextureType; Color: integer): integer; overload; - function AddStatic(X, Y, W, H, Z: real; ColR, ColG, ColB: real; const Name: string; Typ: TTextureType; Color: integer): integer; overload; - function AddStatic(X, Y, W, H, Z: real; ColR, ColG, ColB: real; TexX1, TexY1, TexX2, TexY2: real; const Name: string; Typ: TTextureType; Color: integer; Reflection: boolean; ReflectionSpacing: real): integer; overload; + function AddStatic(X, Y, W, H: real; const TexName: IPath): integer; overload; + function AddStatic(X, Y, W, H: real; const TexName: IPath; Typ: TTextureType): integer; overload; + function AddStatic(X, Y, W, H: real; ColR, ColG, ColB: real; const TexName: IPath; Typ: TTextureType): integer; overload; + function AddStatic(X, Y, W, H, Z: real; ColR, ColG, ColB: real; const TexName: IPath; Typ: TTextureType): integer; overload; + function AddStatic(X, Y, W, H: real; ColR, ColG, ColB: real; const TexName: IPath; Typ: TTextureType; Color: integer): integer; overload; + function AddStatic(X, Y, W, H, Z: real; ColR, ColG, ColB: real; const TexName: IPath; Typ: TTextureType; Color: integer): integer; overload; + function AddStatic(X, Y, W, H, Z: real; ColR, ColG, ColB: real; TexX1, TexY1, TexX2, TexY2: real; const TexName: IPath; Typ: TTextureType; Color: integer; Reflection: boolean; ReflectionSpacing: real): integer; overload; // text function AddText(ThemeText: TThemeText): integer; overload; - function AddText(X, Y: real; const Text_: string): integer; overload; - function AddText(X, Y: real; Style: integer; Size, ColR, ColG, ColB: real; const Text: string): integer; overload; - function AddText(X, Y, W: real; Style: integer; Size, ColR, ColG, ColB: real; Align: integer; const Text_: string; Reflection_: boolean; ReflectionSpacing_: real; Z : real): integer; overload; + function AddText(X, Y: real; const Text_: UTF8String): integer; overload; + function AddText(X, Y: real; Style: integer; Size, ColR, ColG, ColB: real; const Text: UTF8String): integer; overload; + function AddText(X, Y, W: real; Style: integer; Size, ColR, ColG, ColB: real; Align: integer; const Text_: UTF8String; Reflection_: boolean; ReflectionSpacing_: real; Z : real): integer; overload; // button - Procedure SetButtonLength(Length: cardinal); //Function that Set Length of Button Array in one Step instead of register new Memory for every Button + procedure SetButtonLength(Length: cardinal); //Function that Set Length of Button Array in one Step instead of register new Memory for every Button function AddButton(ThemeButton: TThemeButton): integer; overload; - function AddButton(X, Y, W, H: real; const Name: string): integer; overload; - function AddButton(X, Y, W, H: real; const Name: string; Typ: TTextureType; Reflection: boolean): integer; overload; - function AddButton(X, Y, W, H, ColR, ColG, ColB, Int, DColR, DColG, DColB, DInt: real; const Name: string; Typ: TTextureType; Reflection: boolean; ReflectionSpacing, DeSelectReflectionSpacing: real): integer; overload; + function AddButton(X, Y, W, H: real; const TexName: IPath): integer; overload; + function AddButton(X, Y, W, H: real; const TexName: IPath; Typ: TTextureType; Reflection: boolean): integer; overload; + function AddButton(X, Y, W, H, ColR, ColG, ColB, Int, DColR, DColG, DColB, DInt: real; const TexName: IPath; Typ: TTextureType; Reflection: boolean; ReflectionSpacing, DeSelectReflectionSpacing: real): integer; overload; procedure ClearButtons; - procedure AddButtonText(AddX, AddY: real; const AddText: string); overload; - procedure AddButtonText(AddX, AddY: real; ColR, ColG, ColB: real; const AddText: string); overload; - procedure AddButtonText(AddX, AddY: real; ColR, ColG, ColB: real; Font: integer; Size: integer; Align: integer; const AddText: string); overload; - procedure AddButtonText(CustomButton: TButton; AddX, AddY: real; ColR, ColG, ColB: real; Font: integer; Size: integer; Align: integer; const AddText: string); overload; + procedure AddButtonText(AddX, AddY: real; const AddText: UTF8String); overload; + procedure AddButtonText(AddX, AddY: real; ColR, ColG, ColB: real; const AddText: UTF8String); overload; + procedure AddButtonText(AddX, AddY: real; ColR, ColG, ColB: real; Font: integer; Size: integer; Align: integer; const AddText: UTF8String); overload; + procedure AddButtonText(CustomButton: TButton; AddX, AddY: real; ColR, ColG, ColB: real; Font: integer; Size: integer; Align: integer; const AddText: UTF8String); overload; // select slide - function AddSelectSlide(ThemeSelectS: TThemeSelectSlide; var Data: integer; Values: array of string): integer; overload; + function AddSelectSlide(ThemeSelectS: TThemeSelectSlide; var Data: integer; const Values: array of UTF8String): integer; overload; function AddSelectSlide(X, Y, W, H, SkipX, SBGW, ColR, ColG, ColB, Int, DColR, DColG, DColB, DInt, TColR, TColG, TColB, TInt, TDColR, TDColG, TDColB, TDInt, SBGColR, SBGColG, SBGColB, SBGInt, SBGDColR, SBGDColG, SBGDColB, SBGDInt, STColR, STColG, STColB, STInt, STDColR, STDColG, STDColB, STDInt: real; - const Name: string; Typ: TTextureType; const SBGName: string; SBGTyp: TTextureType; - const Caption: string; var Data: integer): integer; overload; - procedure AddSelectSlideOption(const AddText: string); overload; - procedure AddSelectSlideOption(SelectNo: cardinal; const AddText: string); overload; - procedure UpdateSelectSlideOptions(ThemeSelectSlide: TThemeSelectSlide; SelectNum: integer; Values: array of string; var Data: integer); + const TexName: IPath; Typ: TTextureType; const SBGName: IPath; SBGTyp: TTextureType; + const Caption: UTF8String; var Data: integer): integer; overload; + procedure AddSelectSlideOption(const AddText: UTF8String); overload; + procedure AddSelectSlideOption(SelectNo: cardinal; const AddText: UTF8String); overload; + procedure UpdateSelectSlideOptions(ThemeSelectSlide: TThemeSelectSlide; SelectNum: integer; const Values: array of UTF8String; var Data: integer); // function AddWidget(X, Y : UInt16; WidgetSrc : PSDL_Surface): Int16; // procedure ClearWidgets(MinNumber : Int16); procedure FadeTo(Screen: PMenu); overload; procedure FadeTo(Screen: PMenu; aSound: TAudioPlaybackStream); overload; //popup hack - procedure CheckFadeTo(Screen: PMenu; msg: string); + procedure CheckFadeTo(Screen: PMenu; Msg: UTF8String); function DrawBG: boolean; virtual; function DrawFG: boolean; virtual; function Draw: boolean; virtual; - function ParseInput(PressedKey: cardinal; CharCode: WideChar; PressedDown : boolean): boolean; virtual; - // FIXME: ParseMouse is not implemented in any subclass and not even used anywhere in the code - // -> do this before activation of this method - //function ParseMouse(Typ: integer; X: integer; Y: integer): boolean; virtual; abstract; - procedure onShow; virtual; - procedure onShowFinish; virtual; - procedure onHide; virtual; + function ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown : boolean): boolean; virtual; + function ParseMouse(MouseButton: integer; BtnDown: boolean; X, Y: integer): boolean; virtual; + function InRegion(X, Y: real; A: TMouseOverRect): boolean; + function InteractAt(X, Y: real): integer; + function CollectionAt(X, Y: real): integer; + procedure OnShow; virtual; + procedure OnShowFinish; virtual; + procedure OnHide; virtual; procedure SetAnimationProgress(Progress: real); virtual; @@ -167,13 +169,16 @@ type end; const - pmMove = 1; - pmClick = 2; + MENU_MDOWN = 8; + MENU_MUP = 0; + + pmMove = 1; + pmClick = 2; pmUnClick = 3; - iButton = 0; // interaction type - iText = 2; - iSelectS = 3; + iButton = 0; // interaction type + iText = 2; + iSelectS = 3; iBCollectionChild = 5; // fBlack = 0; // fade type @@ -181,21 +186,22 @@ const implementation -uses UCommon, - ULog, - UMain, - UDrawTexture, - UGraphic, - UDisplay, - UCovers, - UTime, - USkins, - //Background types - UMenuBackgroundNone, - UMenuBackgroundColor, - UMenuBackgroundTexture, - UMenuBackgroundVideo, - UMenuBackgroundFade; +uses + UCommon, + UCovers, + UDisplay, + UDrawTexture, + UGraphic, + ULog, + UMain, + USkins, + UTime, + //Background types + UMenuBackgroundNone, + UMenuBackgroundColor, + UMenuBackgroundTexture, + UMenuBackgroundVideo, + UMenuBackgroundFade; destructor TMenu.Destroy; begin @@ -220,6 +226,8 @@ begin ButtonPos := -1; Background := nil; + + RightMbESC := true; end; { constructor TMenu.Create(Back: string); @@ -251,7 +259,7 @@ begin BackH := H; end; } -function RGBFloatToInt(R, G, B: Double): cardinal; +function RGBFloatToInt(R, G, B: double): cardinal; begin Result := (Trunc(255 * R) shl 16) or (Trunc(255 * G) shl 8) or @@ -290,8 +298,8 @@ begin begin Button[OldNum].Selected := false; - //Deselect Collection if Next Button is Not from Collection - if (NewTyp <> iButton) Or (Button[NewNum].Parent <> Button[OldNum].Parent) then + // deselect collection if next button is not from collection + if (NewTyp <> iButton) or (Button[NewNum].Parent <> Button[OldNum].Parent) then ButtonCollection[Button[OldNum].Parent-1].Selected := false; end; end; @@ -339,8 +347,9 @@ procedure TMenu.AddBackground(ThemedSettings: TThemeBackground); var FileExt: string; - Function IsInArray(const Piece: string; const A: array of string): boolean; - var I: integer; + function IsInArray(const Piece: string; const A: array of string): boolean; + var + I: integer; begin Result := false; @@ -352,7 +361,7 @@ procedure TMenu.AddBackground(ThemedSettings: TThemeBackground); end; end; - Function TryBGCreate(Typ: cMenuBackground): boolean; + function TryBGCreate(Typ: cMenuBackground): boolean; begin Result := true; @@ -374,14 +383,14 @@ begin Background := nil; end; - Case ThemedSettings.BGType of + case ThemedSettings.BGType of bgtAuto: begin //Automaticly choose one out of BGT_Texture, BGT_Video or BGT_Color if (Length(ThemedSettings.Tex) > 0) then begin //At first some intelligent try to decide which BG to load - FileExt := lowercase(ExtractFileExt(Skin.GetTextureFileName(ThemedSettings.Tex))); + FileExt := LowerCase(Skin.GetTextureFileName(ThemedSettings.Tex).GetExtension.ToUTF8); if IsInArray(FileExt, SUPPORTED_EXTS_BACKGROUNDTEXTURE) then TryBGCreate(TMenuBackgroundTexture) @@ -491,7 +500,7 @@ end; //---------------------- procedure TMenu.AddButtonCollection(const ThemeCollection: TThemeButtonCollection; const Num: byte); var - BT, BTLen: integer; + BT, BTLen: integer; TempCol, TempDCol: cardinal; begin @@ -589,29 +598,37 @@ begin ThemeStatic.Typ, $FFFFFF, ThemeStatic.Reflection, ThemeStatic.Reflectionspacing); end; -function TMenu.AddStatic(X, Y, W, H: real; const Name: string): integer; +function TMenu.AddStatic(X, Y, W, H: real; const TexName: IPath): integer; begin - Result := AddStatic(X, Y, W, H, Name, TEXTURE_TYPE_PLAIN); + Result := AddStatic(X, Y, W, H, TexName, TEXTURE_TYPE_PLAIN); end; -function TMenu.AddStatic(X, Y, W, H: real; ColR, ColG, ColB: real; const Name: string; Typ: TTextureType): integer; +function TMenu.AddStatic(X, Y, W, H: real; + ColR, ColG, ColB: real; + const TexName: IPath; + Typ: TTextureType): integer; begin - Result := AddStatic(X, Y, W, H, ColR, ColG, ColB, Name, Typ, $FFFFFF); + Result := AddStatic(X, Y, W, H, ColR, ColG, ColB, TexName, Typ, $FFFFFF); end; -function TMenu.AddStatic(X, Y, W, H, Z: real; ColR, ColG, ColB: real; const Name: string; Typ: TTextureType): integer; +function TMenu.AddStatic(X, Y, W, H, Z: real; + ColR, ColG, ColB: real; + const TexName: IPath; + Typ: TTextureType): integer; begin - Result := AddStatic(X, Y, W, H, Z, ColR, ColG, ColB, Name, Typ, $FFFFFF); + Result := AddStatic(X, Y, W, H, Z, ColR, ColG, ColB, TexName, Typ, $FFFFFF); end; -function TMenu.AddStatic(X, Y, W, H: real; const Name: string; Typ: TTextureType): integer; +function TMenu.AddStatic(X, Y, W, H: real; + const TexName: IPath; + Typ: TTextureType): integer; var StatNum: integer; begin // adds static StatNum := Length(Static); SetLength(Static, StatNum + 1); - Static[StatNum] := TStatic.Create(Texture.GetTexture(Name, Typ, $FF00FF)); // new skin + Static[StatNum] := TStatic.Create(Texture.GetTexture(TexName, Typ, $FF00FF)); // new skin // configures static Static[StatNum].Texture.X := X; @@ -622,17 +639,32 @@ begin Result := StatNum; end; -function TMenu.AddStatic(X, Y, W, H: real; ColR, ColG, ColB: real; const Name: string; Typ: TTextureType; Color: integer): integer; +function TMenu.AddStatic(X, Y, W, H: real; + ColR, ColG, ColB: real; + const TexName: IPath; + Typ: TTextureType; + Color: integer): integer; begin - Result := AddStatic(X, Y, W, H, 0, ColR, ColG, ColB, Name, Typ, Color); + Result := AddStatic(X, Y, W, H, 0, ColR, ColG, ColB, TexName, Typ, Color); end; -function TMenu.AddStatic(X, Y, W, H, Z: real; ColR, ColG, ColB: real; const Name: string; Typ: TTextureType; Color: integer): integer; +function TMenu.AddStatic(X, Y, W, H, Z: real; + ColR, ColG, ColB: real; + const TexName: IPath; + Typ: TTextureType; + Color: integer): integer; begin - Result := AddStatic(X, Y, W, H, Z, ColR, ColG, ColB, 0, 0, 1, 1, Name, Typ, Color, false, 0); + Result := AddStatic(X, Y, W, H, Z, ColR, ColG, ColB, 0, 0, 1, 1, TexName, Typ, Color, false, 0); end; -function TMenu.AddStatic(X, Y, W, H, Z: real; ColR, ColG, ColB: real; TexX1, TexY1, TexX2, TexY2: real; const Name: string; Typ: TTextureType; Color: integer; Reflection: boolean; ReflectionSpacing: real): integer; +function TMenu.AddStatic(X, Y, W, H, Z: real; + ColR, ColG, ColB: real; + TexX1, TexY1, TexX2, TexY2: real; + const TexName: IPath; + Typ: TTextureType; + Color: integer; + Reflection: boolean; + ReflectionSpacing: real): integer; var StatNum: integer; begin @@ -644,18 +676,28 @@ begin if (Typ = TEXTURE_TYPE_COLORIZED) then begin // give encoded color to GetTexture() - Static[StatNum] := TStatic.Create(Texture.GetTexture(Name, Typ, RGBFloatToInt(ColR, ColG, ColB))); + Static[StatNum] := TStatic.Create(Texture.GetTexture(TexName, Typ, RGBFloatToInt(ColR, ColG, ColB))); end else begin - Static[StatNum] := TStatic.Create(Texture.GetTexture(Name, Typ, Color)); // new skin + Static[StatNum] := TStatic.Create(Texture.GetTexture(TexName, Typ, Color)); // new skin end; - + // configures static Static[StatNum].Texture.X := X; Static[StatNum].Texture.Y := Y; - Static[StatNum].Texture.W := W; - Static[StatNum].Texture.H := H; + + //Set height and width via sprite size if omitted + if(H = 0) then + Static[StatNum].Texture.H := Static[StatNum].Texture.H + else + Static[StatNum].Texture.H := H; + + if(W = 0) then + Static[StatNum].Texture.W := Static[StatNum].Texture.W + else + Static[StatNum].Texture.W := W; + Static[StatNum].Texture.Z := Z; if (Typ <> TEXTURE_TYPE_COLORIZED) then begin @@ -683,7 +725,7 @@ begin ThemeText.ColR, ThemeText.ColG, ThemeText.ColB, ThemeText.Align, ThemeText.Text, ThemeText.Reflection, ThemeText.ReflectionSpacing, ThemeText.Z); end; -function TMenu.AddText(X, Y: real; const Text_: string): integer; +function TMenu.AddText(X, Y: real; const Text_: UTF8String): integer; var TextNum: integer; begin @@ -694,12 +736,22 @@ begin Result := TextNum; end; -function TMenu.AddText(X, Y: real; Style: integer; Size, ColR, ColG, ColB: real; const Text: string): integer; +function TMenu.AddText(X, Y: real; + Style: integer; + Size, ColR, ColG, ColB: real; + const Text: UTF8String): integer; begin Result := AddText(X, Y, 0, Style, Size, ColR, ColG, ColB, 0, Text, false, 0, 0); end; -function TMenu.AddText(X, Y, W: real; Style: integer; Size, ColR, ColG, ColB: real; Align: integer; const Text_: string; Reflection_: boolean; ReflectionSpacing_: real; Z : real): integer; +function TMenu.AddText(X, Y, W: real; + Style: integer; + Size, ColR, ColG, ColB: real; + Align: integer; + const Text_: UTF8String; + Reflection_: boolean; + ReflectionSpacing_: real; + Z : real): integer; var TextNum: integer; begin @@ -711,9 +763,9 @@ begin end; //Function that Set Length of Button boolean in one Step instead of register new Memory for every Button -Procedure TMenu.SetButtonLength(Length: cardinal); +procedure TMenu.SetButtonLength(Length: cardinal); begin - if (ButtonPos = -1) AND (Length > 0) then + if (ButtonPos = -1) and (Length > 0) then begin //Set Length of Button SetLength(Button, Length); @@ -769,10 +821,10 @@ begin ThemeButton.Text[BT].Text); end; - //BAutton Collection Mod + // bautton collection mod if (ThemeButton.Parent <> 0) then begin - //If Collection Exists then Change Interaction to Child Button + // if collection exists then change interaction to child button if (@ButtonCollection[ThemeButton.Parent-1] <> nil) then begin Interactions[High(Interactions)].Typ := iBCollectionChild; @@ -790,19 +842,21 @@ begin Log.LogBenchmark('====> Screen Options32', 6); end; -function TMenu.AddButton(X, Y, W, H: real; const Name: string): integer; +function TMenu.AddButton(X, Y, W, H: real; const TexName: IPath): integer; begin - Result := AddButton(X, Y, W, H, Name, TEXTURE_TYPE_PLAIN, false); + Result := AddButton(X, Y, W, H, TexName, TEXTURE_TYPE_PLAIN, false); end; -function TMenu.AddButton(X, Y, W, H: real; const Name: string; Typ: TTextureType; Reflection: boolean): integer; +function TMenu.AddButton(X, Y, W, H: real; const TexName: IPath; Typ: TTextureType; Reflection: boolean): integer; begin - Result := AddButton(X, Y, W, H, 1, 1, 1, 1, 1, 1, 1, 0.5, Name, TEXTURE_TYPE_PLAIN, Reflection, 15, 15); + Result := AddButton(X, Y, W, H, 1, 1, 1, 1, 1, 1, 1, 0.5, TexName, TEXTURE_TYPE_PLAIN, Reflection, 15, 15); end; function TMenu.AddButton(X, Y, W, H, ColR, ColG, ColB, Int, DColR, DColG, DColB, DInt: real; - const Name: string; Typ: TTextureType; - Reflection: boolean; ReflectionSpacing, DeSelectReflectionSpacing: real): integer; + const TexName: IPath; + Typ: TTextureType; + Reflection: boolean; + ReflectionSpacing, DeSelectReflectionSpacing: real): integer; begin // adds button //SetLength is used once to reduce Memory usement @@ -821,12 +875,12 @@ begin if (Typ = TEXTURE_TYPE_COLORIZED) then begin // give encoded color to GetTexture() - Button[Result] := TButton.Create(Texture.GetTexture(Name, Typ, RGBFloatToInt(ColR, ColG, ColB)), - Texture.GetTexture(Name, Typ, RGBFloatToInt(DColR, DColG, DColB))); + Button[Result] := TButton.Create(Texture.GetTexture(TexName, Typ, RGBFloatToInt(ColR, ColG, ColB)), + Texture.GetTexture(TexName, Typ, RGBFloatToInt(DColR, DColG, DColB))); end else begin - Button[Result] := TButton.Create(Texture.GetTexture(Name, Typ)); + Button[Result] := TButton.Create(Texture.GetTexture(TexName, Typ)); end; // configures button @@ -855,7 +909,7 @@ begin Button[Result].Reflectionspacing := ReflectionSpacing; Button[Result].DeSelectReflectionspacing := DeSelectReflectionSpacing; - //Button Collection Mod + // button collection mod Button[Result].Parent := 0; // adds interaction @@ -868,11 +922,10 @@ begin Setlength(Button, 0); end; -// Method to draw our TMenu and all his child buttons +// method to draw our tmenu and all his child buttons function TMenu.DrawBG: boolean; begin Background.Draw; - Result := true; end; @@ -881,11 +934,11 @@ var J: integer; begin // We don't forget about newly implemented static for nice skin ... - for J := 0 to Length(Static) - 1 do + for J := 0 to High(Static) do Static[J].Draw; // ... and slightly implemented menutext unit - for J := 0 to Length(Text) - 1 do + for J := 0 to High(Text) do Text[J].Draw; // Draw all ButtonCollections @@ -893,10 +946,10 @@ begin ButtonCollection[J].Draw; // Second, we draw all of our buttons - for J := 0 to Length(Button) - 1 do + for J := 0 to High(Button) do Button[J].Draw; - for J := 0 to Length(SelectsS) - 1 do + for J := 0 to High(SelectsS) do SelectsS[J].Draw; // Third, we draw all our widgets @@ -920,9 +973,9 @@ end; } { -function TMenu.AddWidget(X, Y : UInt16; WidgetSrc : PSDL_Surface): Int16; +function TMenu.AddWidget(X, Y: UInt16; WidgetSrc: PSDL_Surface): Int16; var - WidgetNum : Int16; + WidgetNum: Int16; begin if (Assigned(WidgetSrc)) then begin @@ -946,9 +999,9 @@ end; } { -procedure TMenu.ClearWidgets(MinNumber : Int16); +procedure TMenu.ClearWidgets(MinNumber: Int16); var - J : Int16; + J: Int16; begin for J := MinNumber to (Length(WidgetsSrc) - 1) do begin @@ -991,9 +1044,10 @@ begin Int := Int - ceil(Length(Interactions) / 2); //Set Interaction - if ((Int < 0) or (Int > Length(Interactions) - 1)) - then Int := Interaction //nonvalid button, keep current one - else Interaction := Int; //select row above + if ((Int < 0) or (Int > Length(Interactions) - 1)) then + Int := Interaction // invalid button, keep current one + else + Interaction := Int; // select row above end; procedure TMenu.InteractNextRow; @@ -1005,9 +1059,10 @@ begin Int := Int + ceil(Length(Interactions) / 2); //Set Interaction - if ((Int < 0) or (Int > Length(Interactions) - 1)) - then Int := Interaction //nonvalid button, keep current one - else Interaction := Int; //select row above + if ((Int < 0) or (Int > Length(Interactions) - 1)) then + Int := Interaction // invalid button, keep current one + else + Interaction := Int; // select row above end; procedure TMenu.InteractNext; @@ -1021,7 +1076,8 @@ begin Int := (Int + 1) mod Length(Interactions); //If no Interaction is Selectable Simply Select Next - if (Int = Interaction) then Break; + if (Int = Interaction) then + Break; until IsSelectable(Int); @@ -1038,10 +1094,12 @@ begin // change interaction as long as it's needed repeat Int := Int - 1; - if Int = -1 then Int := High(Interactions); + if Int = -1 then + Int := High(Interactions); //If no Interaction is Selectable Simply Select Next - if (Int = Interaction) then Break; + if (Int = Interaction) then + Break; until IsSelectable(Int); //Set Interaction @@ -1066,7 +1124,8 @@ begin while (Again = true) do begin Num := SelInteraction - CustomSwitch; - if Num = -1 then Num := High(Interactions); + if Num = -1 then + Num := High(Interactions); Interaction := Num; Again := false; // reset, default to accept changing interaction @@ -1118,21 +1177,41 @@ begin AudioPlayback.PlaySound( aSound ); end; +procedure OnSaveEncodingError(Value: boolean; Data: Pointer); +begin + Display.CheckOK := Value; + if (Value) then + begin + //Hack to Finish Singscreen correct on Exit with Q Shortcut + if (Display.NextScreenWithCheck = nil) then + begin + if (Display.CurrentScreen = @ScreenSing) then + ScreenSing.Finish + {else if (Display.CurrentScreen = @ScreenSingModi) then + ScreenSingModi.Finish;} + end; + end + else + begin + Display.NextScreenWithCheck := nil; + end; +end; + //popup hack -procedure TMenu.CheckFadeTo(Screen: PMenu; msg: string); +procedure TMenu.CheckFadeTo(Screen: PMenu; Msg: UTF8String); begin Display.Fade := 0; Display.NextScreenWithCheck := Screen; Display.CheckOK := false; - ScreenPopupCheck.ShowPopup(msg); + ScreenPopupCheck.ShowPopup(msg, OnSaveEncodingError, nil, false); end; -procedure TMenu.AddButtonText(AddX, AddY: real; const AddText: string); +procedure TMenu.AddButtonText(AddX, AddY: real; const AddText: UTF8String); begin AddButtonText(AddX, AddY, 1, 1, 1, AddText); end; -procedure TMenu.AddButtonText(AddX, AddY: real; ColR, ColG, ColB: real; const AddText: string); +procedure TMenu.AddButtonText(AddX, AddY: real; ColR, ColG, ColB: real; const AddText: UTF8String); var Il: integer; begin @@ -1148,7 +1227,7 @@ begin end; end; -procedure TMenu.AddButtonText(AddX, AddY: real; ColR, ColG, ColB: real; Font: integer; Size: integer; Align: integer; const AddText: string); +procedure TMenu.AddButtonText(AddX, AddY: real; ColR, ColG, ColB: real; Font: integer; Size: integer; Align: integer; const AddText: UTF8String); var Il: integer; begin @@ -1167,7 +1246,7 @@ begin end; end; -procedure TMenu.AddButtonText(CustomButton: TButton; AddX, AddY: real; ColR, ColG, ColB: real; Font: integer; Size: integer; Align: integer; const AddText: string); +procedure TMenu.AddButtonText(CustomButton: TButton; AddX, AddY: real; ColR, ColG, ColB: real; Font: integer; Size: integer; Align: integer; const AddText: UTF8String); var Il: integer; begin @@ -1186,7 +1265,7 @@ begin end; end; -function TMenu.AddSelectSlide(ThemeSelectS: TThemeSelectSlide; var Data: integer; Values: array of string): integer; +function TMenu.AddSelectSlide(ThemeSelectS: TThemeSelectSlide; var Data: integer; const Values: array of UTF8String): integer; var SO: integer; begin @@ -1210,6 +1289,9 @@ begin SelectsS[High(SelectsS)].Texture.Z := ThemeSelectS.Z; SelectsS[High(SelectsS)].TextureSBG.Z := ThemeSelectS.Z; + SelectsS[High(SelectsS)].showArrows := ThemeSelectS.showArrows; + SelectsS[High(SelectsS)].oneItemOnly := ThemeSelectS.oneItemOnly; + //Generate Lines SelectsS[High(SelectsS)].GenLines; @@ -1220,8 +1302,8 @@ function TMenu.AddSelectSlide(X, Y, W, H, SkipX, SBGW, ColR, ColG, ColB, Int, DC TColR, TColG, TColB, TInt, TDColR, TDColG, TDColB, TDInt, SBGColR, SBGColG, SBGColB, SBGInt, SBGDColR, SBGDColG, SBGDColB, SBGDInt, STColR, STColG, STColB, STInt, STDColR, STDColG, STDColB, STDInt: real; - const Name: string; Typ: TTextureType; const SBGName: string; SBGTyp: TTextureType; - const Caption: string; var Data: integer): integer; + const TexName: IPath; Typ: TTextureType; const SBGName: IPath; SBGTyp: TTextureType; + const Caption: UTF8String; var Data: integer): integer; var S: integer; I: integer; @@ -1231,9 +1313,9 @@ begin SelectsS[S] := TSelectSlide.Create; if (Typ = TEXTURE_TYPE_COLORIZED) then - SelectsS[S].Texture := Texture.GetTexture(Name, Typ, RGBFloatToInt(ColR, ColG, ColB)) + SelectsS[S].Texture := Texture.GetTexture(TexName, Typ, RGBFloatToInt(ColR, ColG, ColB)) else - SelectsS[S].Texture := Texture.GetTexture(Name, Typ); + SelectsS[S].Texture := Texture.GetTexture(TexName, Typ); SelectsS[S].X := X; SelectsS[S].Y := Y; SelectsS[S].W := W; @@ -1252,9 +1334,21 @@ begin SelectsS[S].TextureSBG := Texture.GetTexture(SBGName, SBGTyp, RGBFloatToInt(SBGColR, SBGColG, SBGColB)) else SelectsS[S].TextureSBG := Texture.GetTexture(SBGName, SBGTyp); + + SelectsS[High(SelectsS)].Tex_SelectS_ArrowL := Tex_SelectS_ArrowL; + SelectsS[High(SelectsS)].Tex_SelectS_ArrowL.X := X + W + SkipX; + SelectsS[High(SelectsS)].Tex_SelectS_ArrowL.Y := Y; + SelectsS[High(SelectsS)].Tex_SelectS_ArrowL.W := Tex_SelectS_ArrowL.W; + SelectsS[High(SelectsS)].Tex_SelectS_ArrowL.H := Tex_SelectS_ArrowL.H; + + SelectsS[High(SelectsS)].Tex_SelectS_ArrowR := Tex_SelectS_ArrowR; + SelectsS[High(SelectsS)].Tex_SelectS_ArrowR.X := X + W + SkipX + SBGW - Tex_SelectS_ArrowR.W; + SelectsS[High(SelectsS)].Tex_SelectS_ArrowR.Y := Y; + SelectsS[High(SelectsS)].Tex_SelectS_ArrowR.W := Tex_SelectS_ArrowR.W; + SelectsS[High(SelectsS)].Tex_SelectS_ArrowR.H := Tex_SelectS_ArrowR.H; + SelectsS[S].TextureSBG.X := X + W + SkipX; SelectsS[S].TextureSBG.Y := Y; - //SelectsS[S].TextureSBG.W := 450; SelectsS[S].SBGW := SBGW; SelectsS[S].TextureSBG.H := H; SelectsS[S].SBGColR := SBGColR; @@ -1339,12 +1433,12 @@ begin Result := S; end; -procedure TMenu.AddSelectSlideOption(const AddText: string); +procedure TMenu.AddSelectSlideOption(const AddText: UTF8String); begin AddSelectSlideOption(High(SelectsS), AddText); end; -procedure TMenu.AddSelectSlideOption(SelectNo: cardinal; const AddText: string); +procedure TMenu.AddSelectSlideOption(SelectNo: cardinal; const AddText: UTF8String); var SO: integer; begin @@ -1352,13 +1446,16 @@ begin SetLength(SelectsS[SelectNo].TextOptT, SO + 1); SelectsS[SelectNo].TextOptT[SO] := AddText; +{ + SelectsS[S].SelectedOption := SelectsS[S].SelectOptInt; // refresh - //SelectsS[S].SelectedOption := SelectsS[S].SelectOptInt; // refresh - - //if SO = Selects[S].PData^ then Selects[S].SelectedOption := SO; + if SO = Selects[S].PData^ then + Selects[S].SelectedOption := SO; +} end; -procedure TMenu.UpdateSelectSlideOptions(ThemeSelectSlide: TThemeSelectSlide; SelectNum: integer; Values: array of string; var Data: integer); +procedure TMenu.UpdateSelectSlideOptions(ThemeSelectSlide: TThemeSelectSlide; + SelectNum: integer; const Values: array of UTF8String; var Data: integer); var SO: integer; begin @@ -1455,12 +1552,12 @@ begin end; end; end; - //interact Prev if there is Nothing to Change + // interact prev if there is nothing to change else begin InteractPrev; - //If ButtonCollection with more than 1 Entry then Select Last Entry - if (Button[Interactions[Interaction].Num].Parent <> 0) AND (ButtonCollection[Button[Interactions[Interaction].Num].Parent-1].CountChilds > 1) then + // if buttoncollection with more than 1 entry then select last entry + if (Button[Interactions[Interaction].Num].Parent <> 0) and (ButtonCollection[Button[Interactions[Interaction].Num].Parent-1].CountChilds > 1) then begin //Select Last Child for Num := High(Button) downto 1 do @@ -1483,7 +1580,7 @@ begin AddStatic(X+2, Y+2, W-4, H-4, 1, 1, 1, Skin.GetTextureFileName('MainBar'), TEXTURE_TYPE_COLORIZED); end; -procedure TMenu.onShow; +procedure TMenu.OnShow; begin // FIXME: this needs some work. First, there should be a variable like // VideoBackground so we can check whether a video-background is enabled or not. @@ -1512,60 +1609,149 @@ begin Background.OnShow; end; -procedure TMenu.onShowFinish; +procedure TMenu.OnShowFinish; +begin + // nothing +end; + +procedure TMenu.OnHide; begin // nothing + Background.OnFinish; end; -(* - * Wrapper for WideUpperCase. Needed because some plattforms have problems with - * unicode support. - *) -function TMenu.WideCharUpperCase(wchar: WideChar) : WideString; +function TMenu.ParseInput(PressedKey: Cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; begin - // On Linux and MacOSX the cwstring unit is necessary for Unicode function-calls. - // Otherwise you will get an EIntOverflow exception (thrown by unimplementedwidestring()). - // The Unicode manager cwstring does not work with MacOSX at the moment because - // of missing references to iconv. So we have to use Ansi... for the moment. + // nothing + Result := true; +end; - // cwstring crashes in FPC 2.2.2 so do not use the cwstring stuff - {.$IFNDEF DARWIN} - {$IFDEF NOIGNORE} - // The FPC implementation of WideUpperCase returns nil if wchar is #0 (e.g. if an arrow key is pressed) - if (wchar <> #0) then - Result := WideUpperCase(wchar) - else - Result := #0; - {$ELSE} - Result := AnsiUpperCase(wchar) - {$ENDIF} +function TMenu.ParseMouse(MouseButton: integer; BtnDown: boolean; X, Y: integer): boolean; +var + nBut: integer; + Action: TMouseClickAction; +begin + //default mouse parsing: clicking generates return keypress, + // mousewheel selects in select slide + //override ParseMouse to customize + Result := true; + + if RightMbESC and (MouseButton = SDL_BUTTON_RIGHT) and BtnDown then + begin + //if RightMbESC is set, send ESC keypress + Result:=ParseInput(SDLK_ESCAPE, 0, true); + end; + + nBut := InteractAt(X, Y); + if nBut >= 0 then + begin + //select on mouse-over + if nBut <> Interaction then + SetInteraction(nBut); + + Action := maNone; + + if (BtnDown) then + begin + if (MouseButton = SDL_BUTTON_LEFT) then + begin + //click button or SelectS + if (Interactions[nBut].Typ = iSelectS) then + Action := SelectsS[Interactions[nBut].Num].OnClick((X / Screen.w) * RenderW, (Y / Screen.h) * RenderH) + else + Action := maReturn; + end + else if (MouseButton = SDL_BUTTON_WHEELDOWN) then + begin //forward on select slide with mousewheel + if (Interactions[nBut].Typ = iSelectS) then + Action := maRight; + end + else if (MouseButton = SDL_BUTTON_WHEELUP) then + begin //backward on select slide with mousewheel + if (Interactions[nBut].Typ = iSelectS) then + Action := maLeft; + end; + end; + + // do the action we have to do ;) + case Action of + maReturn: Result := ParseInput(SDLK_RETURN, 0, true); + maLeft: Result := ParseInput(SDLK_LEFT, 0, true); + maRight: Result := ParseInput(SDLK_RIGHT, 0, true); + end; + end + else + begin + nBut := CollectionAt(X, Y); + if (nBut >= 0) and (not ButtonCollection[nBut].Selected) then + begin + // if over button collection, that is not already selected + // -> select first child but don't allow click + nBut := ButtonCollection[nBut].FirstChild - 1; + if nBut <> Interaction then + SetInteraction(nBut); + end; + end; end; -(* - * Wrapper for WideUpperCase. Needed because some plattforms have problems with - * unicode support. - *) -function TMenu.WideStringUpperCase(wstring: WideString) : WideString; +function TMenu.InRegion(X, Y: real; A: TMouseOverRect): boolean; begin - // cwstring crashes in FPC 2.2.2 so do not use the cwstring stuff - {.$IFNDEF DARWIN} - {$IFDEF NOIGNORE} - Result := WideUpperCase(wstring) - {$ELSE} - Result := AnsiUpperCase(wstring); - {$ENDIF} + // transfer mousecords to the 800x600 raster we use to draw + X := (X / Screen.w) * RenderW; + Y := (Y / Screen.h) * RenderH; + + // check whether A contains X and Y + Result := (X >= A.X) and (X <= A.X + A.W) and (Y >= A.Y) and (Y <= A.Y + A.H); end; -procedure TMenu.onHide; +//takes x,y coordinates and returns the interaction number +//of the control at this position +function TMenu.InteractAt(X, Y: real): integer; +var + i, nBut: integer; begin - // nothing - Background.OnFinish; + Result := -1; + for i := Low(Interactions) to High(Interactions) do + begin + case Interactions[i].Typ of + iButton: + if InRegion(X, Y, Button[Interactions[i].Num].GetMouseOverArea) and + Button[Interactions[i].Num].Visible then + begin + Result:=i; + exit; + end; + iBCollectionChild: + if InRegion(X, Y, Button[Interactions[i].Num].GetMouseOverArea) then + begin + Result:=i; + exit; + end; + iSelectS: + if InRegion(X, Y, SelectSs[Interactions[i].Num].GetMouseOverArea) then + begin + Result:=i; + exit; + end; + end; + end; end; -function TMenu.ParseInput(PressedKey: cardinal; CharCode: WideChar; PressedDown: boolean): boolean; +//takes x,y coordinates and returns the button collection id +function TMenu.CollectionAt(X, Y: real): integer; +var + i, nBut: integer; begin - // nothing - Result := true; + Result := -1; + for i:= Low(ButtonCollection) to High(ButtonCollection) do + begin + if InRegion(X, Y, ButtonCollection[i].GetMouseOverArea) and + ButtonCollection[i].Visible then + begin + Result:=i; + exit; + end; + end; end; procedure TMenu.SetAnimationProgress(Progress: real); @@ -1574,4 +1760,3 @@ begin end; end. - diff --git a/Lua/src/menu/UMenuBackground.pas b/Lua/src/menu/UMenuBackground.pas index c85f0806..0e2e63a6 100644 --- a/Lua/src/menu/UMenuBackground.pas +++ b/Lua/src/menu/UMenuBackground.pas @@ -1,83 +1,83 @@ -{* 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 UMenuBackground;
-
-interface
-
-{$IFDEF FPC}
- {$MODE Delphi}
-{$ENDIF}
-
-{$I switches.inc}
-
-uses
- SysUtils,
- UThemes;
-
-//TMenuBackground - abstraction class for MenuBackgrounds
-//this is a class, not an interface because of the constructors
-//and destructors
-//--------
-
-type
- EMenuBackgroundError = class(Exception);
- TMenuBackground = class
- constructor Create(const ThemedSettings: TThemeBackground); virtual;
- procedure OnShow; virtual;
- procedure Draw; virtual;
- procedure OnFinish; virtual;
- destructor Destroy; override;
- end;
- cMenuBackground = class of TMenuBackground;
-
-implementation
-
-constructor TMenuBackground.Create(const ThemedSettings: TThemeBackground);
-begin
- inherited Create;
-end;
-
-destructor TMenuBackground.Destroy;
-begin
- inherited;
-end;
-
-procedure TMenuBackground.OnShow;
-begin
-
-end;
-
-procedure TMenuBackground.OnFinish;
-begin
-
-end;
-
-procedure TMenuBackground.Draw;
-begin
-
-end;
-
-end.
+{* 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 UMenuBackground; + +interface + +{$IFDEF FPC} + {$MODE Delphi} +{$ENDIF} + +{$I switches.inc} + +uses + SysUtils, + UThemes; + +//TMenuBackground - abstraction class for MenuBackgrounds +//this is a class, not an interface because of the constructors +//and destructors +//-------- + +type + EMenuBackgroundError = class(Exception); + TMenuBackground = class + constructor Create(const ThemedSettings: TThemeBackground); virtual; + procedure OnShow; virtual; + procedure Draw; virtual; + procedure OnFinish; virtual; + destructor Destroy; override; + end; + cMenuBackground = class of TMenuBackground; + +implementation + +constructor TMenuBackground.Create(const ThemedSettings: TThemeBackground); +begin + inherited Create; +end; + +destructor TMenuBackground.Destroy; +begin + inherited; +end; + +procedure TMenuBackground.OnShow; +begin + +end; + +procedure TMenuBackground.OnFinish; +begin + +end; + +procedure TMenuBackground.Draw; +begin + +end; + +end. diff --git a/Lua/src/menu/UMenuBackgroundColor.pas b/Lua/src/menu/UMenuBackgroundColor.pas index 68cf2de4..45b58c1e 100644 --- a/Lua/src/menu/UMenuBackgroundColor.pas +++ b/Lua/src/menu/UMenuBackgroundColor.pas @@ -1,69 +1,73 @@ -{* 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 UMenuBackgroundColor;
-
-interface
-
-{$IFDEF FPC}
- {$MODE Delphi}
-{$ENDIF}
-
-{$I switches.inc}
-
-uses
- UThemes,
- UMenuBackground;
-
-//TMenuBackgroundColor - Background Color
-//--------
-
-type
- TMenuBackgroundColor = class (TMenuBackground)
- private
- Color: TRGB;
- public
- constructor Create(const ThemedSettings: TThemeBackground); override;
- procedure Draw; override;
- end;
-
-implementation
-uses
- gl,
- glext;
-
-constructor TMenuBackgroundColor.Create(const ThemedSettings: TThemeBackground);
-begin
- inherited;
- Color := ThemedSettings.Color;
-end;
-
-procedure TMenuBackgroundColor.Draw;
-begin
- glClearColor(Color.R, Color.G, Color.B, 0);
- glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
-end;
-
+{* 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 UMenuBackgroundColor; + +interface + +{$IFDEF FPC} + {$MODE Delphi} +{$ENDIF} + +{$I switches.inc} + +uses + UThemes, + UMenuBackground; + +//TMenuBackgroundColor - Background Color +//-------- + +type + TMenuBackgroundColor = class (TMenuBackground) + private + Color: TRGB; + public + constructor Create(const ThemedSettings: TThemeBackground); override; + procedure Draw; override; + end; + +implementation +uses + gl, + glext, + UGraphic; + +constructor TMenuBackgroundColor.Create(const ThemedSettings: TThemeBackground); +begin + inherited; + Color := ThemedSettings.Color; +end; + +procedure TMenuBackgroundColor.Draw; +begin + if (ScreenAct = 1) then + begin //just clear once, even when using two screens + glClearColor(Color.R, Color.G, Color.B, 0); + glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT); + end; +end; + end.
\ No newline at end of file diff --git a/Lua/src/menu/UMenuBackgroundFade.pas b/Lua/src/menu/UMenuBackgroundFade.pas index b6174738..6d877baa 100644 --- a/Lua/src/menu/UMenuBackgroundFade.pas +++ b/Lua/src/menu/UMenuBackgroundFade.pas @@ -1,170 +1,176 @@ -{* 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 UMenuBackgroundFade;
-
-interface
-
-{$IFDEF FPC}
- {$MODE Delphi}
-{$ENDIF}
-
-{$I switches.inc}
-
-uses
- UThemes,
- UTexture,
- UMenuBackground;
-
-//TMenuBackgroundFade - Background Fade In for Overlay screens
-//--------
-
-type
- TMenuBackgroundFade = class (TMenuBackground)
- private
- Tex: TTexture;
- Color: TRGB;
- Alpha: real;
-
- useTexture: boolean;
-
- FadeTime: cardinal;
- public
- constructor Create(const ThemedSettings: TThemeBackground); override;
- procedure OnShow; override;
- procedure Draw; override;
- destructor Destroy; override;
- end;
-
-const
- FADEINTIME = 1500; //Time the bg fades in
-
-implementation
-uses sdl,
- gl,
- glext,
- USkins,
- UCommon;
-
-constructor TMenuBackgroundFade.Create(const ThemedSettings: TThemeBackground);
-var texFilename: string;
-begin
- inherited;
- FadeTime := 0;
-
- Color := ThemedSettings.Color;
- Alpha := ThemedSettings.Alpha;
- if (Length(ThemedSettings.Tex) > 0) then
- begin
- texFilename := Skin.GetTextureFileName(ThemedSettings.Tex);
- texFilename := AdaptFilePaths(texFilename);
- Tex := Texture.GetTexture(texFilename, TEXTURE_TYPE_PLAIN);
-
- UseTexture := (Tex.TexNum <> 0);
- end
- else
- UseTexture := false;
-
- if (not UseTexture) then
- FreeandNil(Tex);
-end;
-
-destructor TMenuBackgroundFade.Destroy;
-begin
- //Why isn't there any Tex.free method?
- {if UseTexture then
- FreeandNil(Tex); }
- inherited;
-end;
-
-procedure TMenuBackgroundFade.OnShow;
-begin
- FadeTime := SDL_GetTicks;
-end;
-
-procedure TMenuBackgroundFade.Draw;
-var
- Progress: real;
-begin
- if FadeTime = 0 then
- Progress := Alpha
- else
- Progress := Alpha * (SDL_GetTicks - FadeTime) / FADEINTIME;
-
- if Progress > Alpha then
- begin
- FadeTime := 0;
- Progress := Alpha;
- end;
-
- if (UseTexture) then
- begin //Draw Texture to Screen
- glClear(GL_DEPTH_BUFFER_BIT);
-
- glEnable(GL_TEXTURE_2D);
- glEnable(GL_BLEND);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-
- glColorRGB(Color, Progress);
- glBindTexture(GL_TEXTURE_2D, Tex.TexNum);
-
- glBegin(GL_QUADS);
- glTexCoord2f(Tex.TexX1*Tex.TexW, Tex.TexY1*Tex.TexH);
- glVertex2f(0, 0);
-
- glTexCoord2f(Tex.TexX1*Tex.TexW, Tex.TexY2*Tex.TexH);
- glVertex2f(0, 600);
-
- glTexCoord2f(Tex.TexX2*Tex.TexW, Tex.TexY2*Tex.TexH);
- glVertex2f(800, 600);
-
- glTexCoord2f(Tex.TexX2*Tex.TexW, Tex.TexY1*Tex.TexH);
- glVertex2f(800, 0);
- glEnd;
-
- glDisable(GL_BLEND);
- glDisable(GL_TEXTURE_2D);
- end
- else
- begin //Clear Screen w/ progress Alpha + Color
- glClear(GL_DEPTH_BUFFER_BIT);
- glDisable(GL_TEXTURE_2D);
- glEnable(GL_BLEND);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-
- glColorRGB(Color, Progress);
-
- glBegin(GL_QUADS);
- glVertex2f(0, 0);
- glVertex2f(0, 600);
- glVertex2f(800, 600);
- glVertex2f(800, 0);
- glEnd;
-
- glDisable(GL_BLEND);
- end;
-end;
-
-end.
+{* 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 UMenuBackgroundFade; + +interface + +{$IFDEF FPC} + {$MODE Delphi} +{$ENDIF} + +{$I switches.inc} + +uses + UThemes, + UTexture, + UMenuBackground, + UPath; + +//TMenuBackgroundFade - Background Fade In for Overlay screens +//-------- + +type + TMenuBackgroundFade = class (TMenuBackground) + private + Tex: TTexture; + Color: TRGB; + Alpha: real; + + useTexture: boolean; + + FadeTime: cardinal; + public + constructor Create(const ThemedSettings: TThemeBackground); override; + procedure OnShow; override; + procedure Draw; override; + destructor Destroy; override; + end; + +const + FADEINTIME = 1500; //Time the bg fades in + +implementation +uses + sdl, + gl, + glext, + USkins, + UCommon, + UGraphic; + +constructor TMenuBackgroundFade.Create(const ThemedSettings: TThemeBackground); +var + texFilename: IPath; +begin + inherited; + FadeTime := 0; + + Color := ThemedSettings.Color; + Alpha := ThemedSettings.Alpha; + if (Length(ThemedSettings.Tex) > 0) then + begin + texFilename := Skin.GetTextureFileName(ThemedSettings.Tex); + Tex := Texture.GetTexture(texFilename, TEXTURE_TYPE_PLAIN); + + UseTexture := (Tex.TexNum <> 0); + end + else + UseTexture := false; + + if (not UseTexture) then + FreeandNil(Tex); +end; + +destructor TMenuBackgroundFade.Destroy; +begin + //Why isn't there any Tex.free method? + {if UseTexture then + FreeandNil(Tex); } + inherited; +end; + +procedure TMenuBackgroundFade.OnShow; +begin + FadeTime := SDL_GetTicks; +end; + +procedure TMenuBackgroundFade.Draw; +var + Progress: real; +begin + if FadeTime = 0 then + Progress := Alpha + else + Progress := Alpha * (SDL_GetTicks - FadeTime) / FADEINTIME; + + if Progress > Alpha then + begin + FadeTime := 0; + Progress := Alpha; + end; + + if (UseTexture) then + begin //Draw Texture to Screen + if (ScreenAct = 1) then //Clear just once when in dual screen mode + glClear(GL_DEPTH_BUFFER_BIT); + + glEnable(GL_TEXTURE_2D); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + glColorRGB(Color, Progress); + glBindTexture(GL_TEXTURE_2D, Tex.TexNum); + + glBegin(GL_QUADS); + glTexCoord2f(Tex.TexX1*Tex.TexW, Tex.TexY1*Tex.TexH); + glVertex2f(0, 0); + + glTexCoord2f(Tex.TexX1*Tex.TexW, Tex.TexY2*Tex.TexH); + glVertex2f(0, 600); + + glTexCoord2f(Tex.TexX2*Tex.TexW, Tex.TexY2*Tex.TexH); + glVertex2f(800, 600); + + glTexCoord2f(Tex.TexX2*Tex.TexW, Tex.TexY1*Tex.TexH); + glVertex2f(800, 0); + glEnd; + + glDisable(GL_BLEND); + glDisable(GL_TEXTURE_2D); + end + else + begin //Clear Screen w/ progress Alpha + Color + if (ScreenAct = 1) then //Clear just once when in dual screen mode + glClear(GL_DEPTH_BUFFER_BIT); + + glDisable(GL_TEXTURE_2D); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + glColorRGB(Color, Progress); + + glBegin(GL_QUADS); + glVertex2f(0, 0); + glVertex2f(0, 600); + glVertex2f(800, 600); + glVertex2f(800, 0); + glEnd; + + glDisable(GL_BLEND); + end; +end; + +end. diff --git a/Lua/src/menu/UMenuBackgroundNone.pas b/Lua/src/menu/UMenuBackgroundNone.pas index 6b63742a..c64f3023 100644 --- a/Lua/src/menu/UMenuBackgroundNone.pas +++ b/Lua/src/menu/UMenuBackgroundNone.pas @@ -1,68 +1,70 @@ -{* 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 UMenuBackgroundNone;
-
-interface
-
-{$IFDEF FPC}
- {$MODE Delphi}
-{$ENDIF}
-
-{$I switches.inc}
-
-uses
- UThemes,
- UMenuBackground;
-
-//TMenuBackgroundNone - Just no Background (e.g. for Overlays)
-//--------
-
-type
- TMenuBackgroundNone = class (TMenuBackground)
- private
-
- public
- constructor Create(const ThemedSettings: TThemeBackground); override;
- procedure Draw; override;
- end;
-
-implementation
-uses
- gl,
- glext;
-
-constructor TMenuBackgroundNone.Create(const ThemedSettings: TThemeBackground);
-begin
- inherited;
-end;
-
-procedure TMenuBackgroundNone.Draw;
-begin
- //Do just nothing in here!
- glClear(GL_DEPTH_BUFFER_BIT);
-end;
-
+{* 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 UMenuBackgroundNone; + +interface + +{$IFDEF FPC} + {$MODE Delphi} +{$ENDIF} + +{$I switches.inc} + +uses + UThemes, + UMenuBackground; + +//TMenuBackgroundNone - Just no Background (e.g. for Overlays) +//-------- + +type + TMenuBackgroundNone = class (TMenuBackground) + private + + public + constructor Create(const ThemedSettings: TThemeBackground); override; + procedure Draw; override; + end; + +implementation +uses + gl, + glext, + UGraphic; + +constructor TMenuBackgroundNone.Create(const ThemedSettings: TThemeBackground); +begin + inherited; +end; + +procedure TMenuBackgroundNone.Draw; +begin + //Do just nothing in here! + If (ScreenAct = 1) then //Clear just once when in dual screen mode + glClear(GL_DEPTH_BUFFER_BIT); +end; + end.
\ No newline at end of file diff --git a/Lua/src/menu/UMenuBackgroundTexture.pas b/Lua/src/menu/UMenuBackgroundTexture.pas index e8678fc5..f71637ff 100644 --- a/Lua/src/menu/UMenuBackgroundTexture.pas +++ b/Lua/src/menu/UMenuBackgroundTexture.pas @@ -1,122 +1,126 @@ -{* 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 UMenuBackgroundTexture;
-
-interface
-
-{$IFDEF FPC}
- {$MODE Delphi}
-{$ENDIF}
-
-{$I switches.inc}
-
-uses
- UThemes,
- UTexture,
- UMenuBackground;
-
-//TMenuBackgroundColor - Background Color
-//--------
-
-type
- TMenuBackgroundTexture = class (TMenuBackground)
- private
- Tex: TTexture;
- Color: TRGB;
- public
- constructor Create(const ThemedSettings: TThemeBackground); override;
- procedure Draw; override;
- destructor Destroy; override;
- end;
-
-const
- SUPPORTED_EXTS_BACKGROUNDTEXTURE: array[0..13] of string = ('.png', '.bmp', '.jpg', '.jpeg', '.gif', '.pnm', '.ppm', '.pgm', '.pbm', '.xpm', '.lbm', '.pcx', '.tga', '.tiff');
-
-implementation
-uses
- USkins,
- UCommon,
- SysUtils,
- gl,
- glext;
-
-constructor TMenuBackgroundTexture.Create(const ThemedSettings: TThemeBackground);
-var texFilename: string;
-begin
- inherited;
-
- if (Length(ThemedSettings.Tex) = 0) then
- raise EMenuBackgroundError.Create('TMenuBackgroundTexture: No texture filename present');
-
- Color := ThemedSettings.Color;
-
- texFilename := Skin.GetTextureFileName(ThemedSettings.Tex);
- texFilename := AdaptFilePaths(texFilename);
- Tex := Texture.GetTexture(texFilename, TEXTURE_TYPE_PLAIN);
-
- if (Tex.TexNum = 0) then
- begin
- freeandnil(Tex);
- raise EMenuBackgroundError.Create('TMenuBackgroundTexture: Can''t load texture');
- end;
-end;
-
-destructor TMenuBackgroundTexture.Destroy;
-begin
- //freeandnil(Tex); <- this causes an Access Violation o0
- inherited;
-end;
-
-procedure TMenuBackgroundTexture.Draw;
-begin
- glClear(GL_DEPTH_BUFFER_BIT);
- glColorRGB(Color);
-
- glEnable(GL_TEXTURE_2D);
- glEnable(GL_BLEND);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-
- glBindTexture(GL_TEXTURE_2D, Tex.TexNum);
-
- glBegin(GL_QUADS);
- glTexCoord2f(Tex.TexX1*Tex.TexW, Tex.TexY1*Tex.TexH);
- glVertex2f(0, 0);
-
- glTexCoord2f(Tex.TexX1*Tex.TexW, Tex.TexY2*Tex.TexH);
- glVertex2f(0, 600);
-
- glTexCoord2f(Tex.TexX2*Tex.TexW, Tex.TexY2*Tex.TexH);
- glVertex2f(800, 600);
-
- glTexCoord2f(Tex.TexX2*Tex.TexW, Tex.TexY1*Tex.TexH);
- glVertex2f(800, 0);
- glEnd;
-
- glDisable(GL_BLEND);
- glDisable(GL_TEXTURE_2D);
-end;
-
-end.
+{* 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 UMenuBackgroundTexture; + +interface + +{$IFDEF FPC} + {$MODE Delphi} +{$ENDIF} + +{$I switches.inc} + +uses + UThemes, + UTexture, + UMenuBackground, + UPath; + +//TMenuBackgroundColor - Background Color +//-------- + +type + TMenuBackgroundTexture = class (TMenuBackground) + private + Tex: TTexture; + Color: TRGB; + public + constructor Create(const ThemedSettings: TThemeBackground); override; + procedure Draw; override; + destructor Destroy; override; + end; + +const + SUPPORTED_EXTS_BACKGROUNDTEXTURE: array[0..13] of string = ('.png', '.bmp', '.jpg', '.jpeg', '.gif', '.pnm', '.ppm', '.pgm', '.pbm', '.xpm', '.lbm', '.pcx', '.tga', '.tiff'); + +implementation +uses + USkins, + UCommon, + SysUtils, + gl, + glext, + UGraphic; + +constructor TMenuBackgroundTexture.Create(const ThemedSettings: TThemeBackground); +var + texFilename: IPath; +begin + inherited; + + if (Length(ThemedSettings.Tex) = 0) then + raise EMenuBackgroundError.Create('TMenuBackgroundTexture: No texture filename present'); + + Color := ThemedSettings.Color; + + texFilename := Skin.GetTextureFileName(ThemedSettings.Tex); + Tex := Texture.GetTexture(texFilename, TEXTURE_TYPE_PLAIN); + + if (Tex.TexNum = 0) then + begin + freeandnil(Tex); + raise EMenuBackgroundError.Create('TMenuBackgroundTexture: Can''t load texture'); + end; +end; + +destructor TMenuBackgroundTexture.Destroy; +begin + //freeandnil(Tex); <- this causes an Access Violation o0 + inherited; +end; + +procedure TMenuBackgroundTexture.Draw; +begin + If (ScreenAct = 1) then //Clear just once when in dual screen mode + glClear(GL_DEPTH_BUFFER_BIT); + + glColorRGB(Color); + + glEnable(GL_TEXTURE_2D); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + glBindTexture(GL_TEXTURE_2D, Tex.TexNum); + + glBegin(GL_QUADS); + glTexCoord2f(Tex.TexX1*Tex.TexW, Tex.TexY1*Tex.TexH); + glVertex2f(0, 0); + + glTexCoord2f(Tex.TexX1*Tex.TexW, Tex.TexY2*Tex.TexH); + glVertex2f(0, 600); + + glTexCoord2f(Tex.TexX2*Tex.TexW, Tex.TexY2*Tex.TexH); + glVertex2f(800, 600); + + glTexCoord2f(Tex.TexX2*Tex.TexW, Tex.TexY1*Tex.TexH); + glVertex2f(800, 0); + glEnd; + + glDisable(GL_BLEND); + glDisable(GL_TEXTURE_2D); +end; + +end. diff --git a/Lua/src/menu/UMenuBackgroundVideo.pas b/Lua/src/menu/UMenuBackgroundVideo.pas index 377c2170..9d265764 100644 --- a/Lua/src/menu/UMenuBackgroundVideo.pas +++ b/Lua/src/menu/UMenuBackgroundVideo.pas @@ -1,202 +1,203 @@ -{* 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 UMenuBackgroundVideo;
-
-interface
-
-{$IFDEF FPC}
- {$MODE Delphi}
-{$ENDIF}
-
-{$I switches.inc}
-
-uses
- UThemes,
- UMenuBackground,
- UVideo;
-
-//TMenuBackgroundColor - Background Color
-//--------
-
-type
- //DefaultBGVideoPlayback = TVideoPlayback_FFmpeg;
-
-{type
- TBGVideoPool = class;
-
- PBGVideoPoolItem = ^TBGVideoPoolItem;
- TBGVideoPoolItem = record
- Parent: TBGVideoPool;
- VideoPlayback = IVideoPlayback;
- ReferenceCounter: cardinal; //Number of Creations
- end;
-
- TBGVideo = class
- private
- myItem: PBGVideoPoolItem;
- public
- constructor Create(Item: PBGVideoPoolItem); override;
-
- function GetVideoPlayback: IVideoPlayback;
- procedure Draw;
-
- destructor Destroy;
- end;
-
- TBGVideoPool = class
- private
- Items: PBGVideoPoolItem;
- public
- constructor Create;
-
- function GetBGVideo(filename: string): TBGVideo;
- procedure RemoveItem(
- procedure FreeAllItems;
-
- destructor Destroy;
- end;
-
-type }
- TMenuBackgroundVideo = class (TMenuBackground)
- private
- fFilename: string;
- public
- constructor Create(const ThemedSettings: TThemeBackground); override;
- procedure OnShow; override;
- procedure Draw; override;
- procedure OnFinish; override;
- destructor Destroy; override;
- end;
-
-{var
- BGVideoPool: TBGVideoPool; }
-const
- SUPPORTED_EXTS_BACKGROUNDVIDEO: array[0..6] of string = ('.avi', '.mov', '.divx', '.mpg', '.mp4', '.mpeg', '.m2v');
-
-implementation
-
-uses
- gl,
- glext,
- UMusic,
- SysUtils,
- UTime,
- USkins,
- UCommon;
-
-constructor TMenuBackgroundVideo.Create(const ThemedSettings: TThemeBackground);
-begin
- inherited;
- if (Length(ThemedSettings.Tex) = 0) then
- raise EMenuBackgroundError.Create('TMenuBackgroundVideo: No video filename present');
-
- fFileName := Skin.GetTextureFileName(ThemedSettings.Tex);
- fFileName := AdaptFilePaths( fFileName );
-
- if fileexists(fFilename) AND VideoPlayback.Open( fFileName ) then
- begin
- VideoBGTimer.SetTime(0);
- VideoPlayback.Play;
- end
- else
- raise EMenuBackgroundError.Create('TMenuBackgroundVideo: Can''t load background video: ' + fFilename);
-end;
-
-destructor TMenuBackgroundVideo.Destroy;
-begin
-
-end;
-
-procedure TMenuBackgroundVideo.OnShow;
-begin
- if VideoPlayback.Open( fFileName ) then
- begin
- VideoBGTimer.SetTime(0);
- VideoPlayback.Play;
- end;
-end;
-
-procedure TMenuBackgroundVideo.OnFinish;
-begin
-
-end;
-
-procedure TMenuBackgroundVideo.Draw;
-begin
- glClear(GL_DEPTH_BUFFER_BIT);
-
- VideoPlayback.GetFrame(VideoBGTimer.GetTime());
- // FIXME: why do we draw on screen 2? Seems to be wrong.
- VideoPlayback.DrawGL(2);
-end;
-
-// Implementation of TBGVideo
-//--------
-{constructor TBGVideo.Create(Item: PBGVideoPoolItem);
-begin
- myItem := PBGVideoPoolItem;
- Inc(myItem.ReferenceCounter);
-end;
-
-destructor TBGVideo.Destroy;
-begin
- Dec(myItem.ReferenceCounter);
-end;
-
-function TBGVideo.GetVideoPlayback: IVideoPlayback;
-begin
-
-end;
-
-procedure TBGVideo.Draw;
-begin
-
-end;
-
-// Implementation of TBGVideoPool
-//--------
-
-constructor TBGVideoPool.Create;
-begin
-
-end;
-
-destructor TBGVideoPool.Destroy;
-begin
-
-end;
-
-function TBGVideoPool.GetBGVideo(filename: string): TBGVideo;
-begin
-
-end;
-
-procedure TBGVideoPool.FreeAllItems;
-begin
-
-end; }
-
-end.
+{* 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 UMenuBackgroundVideo; + +interface + +{$IFDEF FPC} + {$MODE Delphi} +{$ENDIF} + +{$I switches.inc} + +uses + UThemes, + UMenuBackground, + UVideo, + UPath; + +//TMenuBackgroundColor - Background Color +//-------- + +type + //DefaultBGVideoPlayback = TVideoPlayback_FFmpeg; + +{type + TBGVideoPool = class; + + PBGVideoPoolItem = ^TBGVideoPoolItem; + TBGVideoPoolItem = record + Parent: TBGVideoPool; + VideoPlayback = IVideoPlayback; + ReferenceCounter: cardinal; //Number of Creations + end; + + TBGVideo = class + private + myItem: PBGVideoPoolItem; + public + constructor Create(Item: PBGVideoPoolItem); override; + + function GetVideoPlayback: IVideoPlayback; + procedure Draw; + + destructor Destroy; + end; + + TBGVideoPool = class + private + Items: PBGVideoPoolItem; + public + constructor Create; + + function GetBGVideo(filename: IPath): TBGVideo; + procedure RemoveItem( + procedure FreeAllItems; + + destructor Destroy; + end; + +type } + TMenuBackgroundVideo = class (TMenuBackground) + private + fFilename: IPath; + public + constructor Create(const ThemedSettings: TThemeBackground); override; + procedure OnShow; override; + procedure Draw; override; + procedure OnFinish; override; + destructor Destroy; override; + end; + +{var + BGVideoPool: TBGVideoPool; } +const + SUPPORTED_EXTS_BACKGROUNDVIDEO: array[0..6] of string = ('.avi', '.mov', '.divx', '.mpg', '.mp4', '.mpeg', '.m2v'); + +implementation + +uses + gl, + glext, + UMusic, + SysUtils, + UTime, + USkins, + UCommon, + UGraphic; + +constructor TMenuBackgroundVideo.Create(const ThemedSettings: TThemeBackground); +begin + inherited; + if (Length(ThemedSettings.Tex) = 0) then + raise EMenuBackgroundError.Create('TMenuBackgroundVideo: No video filename present'); + + fFileName := Skin.GetTextureFileName(ThemedSettings.Tex); + if fFilename.IsFile and VideoPlayback.Open(fFileName) then + begin + VideoBGTimer.SetTime(0); + VideoPlayback.Play; + end + else + raise EMenuBackgroundError.Create('TMenuBackgroundVideo: Can''t load background video: ' + fFilename.ToNative); +end; + +destructor TMenuBackgroundVideo.Destroy; +begin + +end; + +procedure TMenuBackgroundVideo.OnShow; +begin + if VideoPlayback.Open( fFileName ) then + begin + VideoBGTimer.SetTime(0); + VideoPlayback.Play; + end; +end; + +procedure TMenuBackgroundVideo.OnFinish; +begin + +end; + +procedure TMenuBackgroundVideo.Draw; +begin + If (ScreenAct = 1) then //Clear just once when in dual screen mode + glClear(GL_DEPTH_BUFFER_BIT); + + VideoPlayback.GetFrame(VideoBGTimer.GetTime()); + // FIXME: why do we draw on screen 2? Seems to be wrong. + VideoPlayback.DrawGL(2); +end; + +// Implementation of TBGVideo +//-------- +{constructor TBGVideo.Create(Item: PBGVideoPoolItem); +begin + myItem := PBGVideoPoolItem; + Inc(myItem.ReferenceCounter); +end; + +destructor TBGVideo.Destroy; +begin + Dec(myItem.ReferenceCounter); +end; + +function TBGVideo.GetVideoPlayback: IVideoPlayback; +begin + +end; + +procedure TBGVideo.Draw; +begin + +end; + +// Implementation of TBGVideoPool +//-------- + +constructor TBGVideoPool.Create; +begin + +end; + +destructor TBGVideoPool.Destroy; +begin + +end; + +function TBGVideoPool.GetBGVideo(filename: IPath): TBGVideo; +begin + +end; + +procedure TBGVideoPool.FreeAllItems; +begin + +end; } + +end. diff --git a/Lua/src/menu/UMenuButton.pas b/Lua/src/menu/UMenuButton.pas index a0cdaeef..868a86f3 100644 --- a/Lua/src/menu/UMenuButton.pas +++ b/Lua/src/menu/UMenuButton.pas @@ -38,31 +38,31 @@ uses UTexture, gl, UMenuText, - SDL; + SDL, + UMenuInteract; type CButton = class of TButton; TButton = class protected - SelectBool: Boolean; + SelectBool: boolean; - FadeProgress: Real; - FadeLastTick: Cardinal; + FadeProgress: real; + FadeLastTick: cardinal; DeSelectW, DeSelectH, PosX, - PosY: Real; + PosY: real; - constructor Create(); overload; public - Text: Array of TText; + Text: array of TText; Texture: TTexture; // Button Screen position and size Texture2: TTexture; // second texture only used for fading full resolution covers - Colorized: Boolean; + Colorized: boolean; DeSelectTexture: TTexture; // texture for colorized hack FadeTex: TTexture; //Texture for beautiful fading @@ -73,15 +73,15 @@ type Reflection: boolean; Reflectionspacing, - DeSelectReflectionspacing: Real; + DeSelectReflectionspacing: real; Fade, - FadeText: Boolean; + FadeText: boolean; Selectable: boolean; //Number of the Parent Collection, 0 if in no Collection - Parent: Byte; + Parent: byte; SelectColR, SelectColG, @@ -103,25 +103,29 @@ type procedure SetW(Value: real); procedure SetH(Value: real); - procedure SetSelect(Value: Boolean); virtual; + procedure SetSelect(Value: boolean); virtual; property X: real read PosX write SetX; property Y: real read PosY write SetY; property Z: real read Texture.z write Texture.z; property W: real read DeSelectW write SetW; property H: real read DeSelectH write SetH; - property Selected: Boolean read SelectBool write SetSelect; + property Selected: boolean read SelectBool write SetSelect; procedure Draw; virtual; + constructor Create(); overload; constructor Create(Textura: TTexture); overload; constructor Create(Textura, DSTexture: TTexture); overload; destructor Destroy; override; + + function GetMouseOverArea: TMouseOverRect; end; implementation -uses SysUtils, - UDrawTexture; +uses + SysUtils, + UDrawTexture; procedure TButton.SetX(Value: real); {var @@ -143,8 +147,8 @@ end; procedure TButton.SetY(Value: real); {var - dY: real; - T: integer; // text} + dY: real; + T: integer; // text} begin {dY := Value - PosY; @@ -164,7 +168,7 @@ begin DeSelectW := Value; - if Not Fade then + if not Fade then begin if SelectBool then Texture.W := SelectW @@ -180,7 +184,7 @@ begin DeSelectH := Value; - if Not Fade then + if not Fade then begin if SelectBool then Texture.H := SelectH @@ -189,9 +193,9 @@ begin end; end; -procedure TButton.SetSelect(Value : Boolean); +procedure TButton.SetSelect(Value : boolean); var - T: integer; + T: integer; begin SelectBool := Value; @@ -251,49 +255,13 @@ begin end; end; -constructor TButton.Create(); -begin - inherited Create; - // We initialize all to 0, nil or false - Visible := true; - SelectBool := false; - DeselectType := 0; - Selectable := true; - Reflection := false; - Colorized := false; - - SelectColR := 1; - SelectColG := 1; - SelectColB := 1; - SelectInt := 1; - SelectTInt := 1; - - DeselectColR := 1; - DeselectColG := 1; - DeselectColB := 1; - DeselectInt := 0.5; - DeselectTInt := 1; - - Fade := false; - FadeTex.TexNum := 0; - FadeProgress := 0; - FadeText := false; - SelectW := DeSelectW; - SelectH := DeSelectH; - - PosX := 0; - PosY := 0; - - Parent := 0; -end; - // ***** Public methods ****** // procedure TButton.Draw; var - T: integer; - Tick: Cardinal; - Spacing: Real; + T: integer; + Tick: cardinal; + Spacing: real; begin if Visible then begin @@ -315,7 +283,7 @@ begin if (FadeText) then begin - For T := 0 to high(Text) do + for T := 0 to high(Text) do begin Text[T].MoveX := (SelectW - DeSelectW) * FadeProgress; Text[T].MoveY := (SelectH - DeSelectH) * FadeProgress; @@ -353,7 +321,7 @@ begin FadeTex.TexY1 := 0; FadeTex.TexY2 := 1; - Case FadeTexPos of + case FadeTexPos of 0: //FadeTex on Top begin //Standard Texture @@ -465,7 +433,7 @@ begin //Reflection Mod if (Reflection) then // Draw Reflections begin - if (FadeProgress <> 0) AND (FadeProgress <> 1) then + if (FadeProgress <> 0) and (FadeProgress <> 1) then begin Spacing := DeSelectReflectionspacing - (DeSelectReflectionspacing - Reflectionspacing) * FadeProgress; end @@ -514,9 +482,10 @@ begin glDisable(GL_TEXTURE_2D); glDisable(GL_DEPTH_TEST); glDisable(GL_BLEND); - end else + end + else with DeSelectTexture do - begin + begin //Bind Tex and GL Attributes glEnable(GL_TEXTURE_2D); glEnable(GL_BLEND); @@ -556,18 +525,98 @@ begin end; end; - for T := 0 to High(Text) do begin + for T := 0 to High(Text) do + begin Text[T].Draw; end; end; end; +function TButton.GetMouseOverArea: TMouseOverRect; +begin + if (FadeTex.TexNum = 0) then + begin + Result.X := Texture.X; + Result.Y := Texture.Y; + Result.W := Texture.W; + Result.H := Texture.H; + end + else + begin + case FadeTexPos of + 0: begin // fade tex on top + Result.X := Texture.X; + Result.Y := FadeTex.Y; + Result.W := Texture.W; + Result.H := FadeTex.H + Texture.H; + end; + + 1: begin // fade tex on left side + Result.X := FadeTex.X; + Result.Y := Texture.Y; + Result.W := FadeTex.W + Texture.W; + Result.H := Texture.H; + end; + + 2: begin // fade tex on bottom + Result.X := Texture.X; + Result.Y := Texture.Y; + Result.W := Texture.W; + Result.H := FadeTex.H + Texture.H; + end; + + 3: begin // fade tex on right side + Result.X := Texture.X; + Result.Y := Texture.Y; + Result.W := FadeTex.W + Texture.W; + Result.H := Texture.H; + end; + end; + end; +end; + destructor TButton.Destroy; begin inherited; end; +constructor TButton.Create(); +begin + inherited Create; + // We initialize all to 0, nil or false + Visible := true; + SelectBool := false; + DeselectType := 0; + Selectable := true; + Reflection := false; + Colorized := false; + + SelectColR := 1; + SelectColG := 1; + SelectColB := 1; + SelectInt := 1; + SelectTInt := 1; + + DeselectColR := 1; + DeselectColG := 1; + DeselectColB := 1; + DeselectInt := 0.5; + DeselectTInt := 1; + + Fade := false; + FadeTex.TexNum := 0; + FadeProgress := 0; + FadeText := false; + SelectW := DeSelectW; + SelectH := DeSelectH; + + PosX := 0; + PosY := 0; + + Parent := 0; +end; + constructor TButton.Create(Textura: TTexture); begin Create(); @@ -577,7 +626,7 @@ begin Texture.ColG := 0.5; Texture.ColB := 0; Texture.Int := 1; - Colorized := False; + Colorized := false; end; // Button has the texture-type "colorized" @@ -592,7 +641,7 @@ begin Texture.ColG := 1; Texture.ColB := 1; Texture.Int := 1; - Colorized := True; + Colorized := true; end; end. diff --git a/Lua/src/menu/UMenuButtonCollection.pas b/Lua/src/menu/UMenuButtonCollection.pas index c6c6dd81..8b7a1c3f 100644 --- a/Lua/src/menu/UMenuButtonCollection.pas +++ b/Lua/src/menu/UMenuButtonCollection.pas @@ -41,61 +41,61 @@ type //TButtonCollection //No Extra Attributes or Functions ATM //---------------- - AButton = Array of TButton; + AButton = array of TButton; PAButton = ^AButton; TButtonCollection = class(TButton) //num of the First Button, that can be Selected - FirstChild: Byte; - CountChilds: Byte; + FirstChild: byte; + CountChilds: byte; ScreenButton: PAButton; - procedure SetSelect(Value : Boolean); override; + procedure SetSelect(Value : boolean); override; procedure Draw; override; end; implementation -procedure TButtonCollection.SetSelect(Value : Boolean); -var I: Integer; +procedure TButtonCollection.SetSelect(Value : boolean); +var + Index: integer; begin inherited; //Set Visible for Every Button that is a Child of this ButtonCollection - if (Not Fade) then - For I := 0 to High(ScreenButton^) do - if (ScreenButton^[I].Parent = Parent) then - ScreenButton^[I].Visible := Value; + if (not Fade) then + for Index := 0 to High(ScreenButton^) do + if (ScreenButton^[Index].Parent = Parent) then + ScreenButton^[Index].Visible := Value; end; procedure TButtonCollection.Draw; -var I, J: Integer; +var + I, J: integer; begin inherited; //If fading is activated, Fade Child Buttons if (Fade) then begin - For I := 0 to High(ScreenButton^) do + for I := 0 to High(ScreenButton^) do if (ScreenButton^[I].Parent = Parent) then begin if (FadeProgress < 0.5) then begin ScreenButton^[I].Visible := SelectBool; - For J := 0 to High(ScreenButton^[I].Text) do + for J := 0 to High(ScreenButton^[I].Text) do ScreenButton^[I].Text[J].Visible := SelectBool; end else begin ScreenButton^[I].Texture.Alpha := (FadeProgress-0.666)*3; - For J := 0 to High(ScreenButton^[I].Text) do + for J := 0 to High(ScreenButton^[I].Text) do ScreenButton^[I].Text[J].Alpha := (FadeProgress-0.666)*3; end; end; end; end; - - end. diff --git a/Lua/src/menu/UMenuEqualizer.pas b/Lua/src/menu/UMenuEqualizer.pas index 6d77721c..8f57e44a 100644 --- a/Lua/src/menu/UMenuEqualizer.pas +++ b/Lua/src/menu/UMenuEqualizer.pas @@ -45,69 +45,71 @@ type Tms_Equalizer = class(TObject) private FFTData: TFFTData; // moved here to avoid stack overflows - BandData: array of Byte; - RefreshTime: Cardinal; + BandData: array of byte; + RefreshTime: cardinal; Source: IAudioPlayback; - Procedure Analyse; + procedure Analyse; public - X: Integer; - Y: Integer; - Z: Real; + X: integer; + Y: integer; + Z: real; - W: Integer; - H: Integer; - Space: Integer; + W: integer; + H: integer; + Space: integer; - Visible: Boolean; - Alpha: real; - Color: TRGB; + Visible: boolean; + Alpha: real; + Color: TRGB; - Direction: Boolean; + Direction: boolean; + BandLength: integer; - BandLength: Integer; - - Reflection: boolean; - Reflectionspacing: Real; + Reflection: boolean; + Reflectionspacing: real; constructor Create(Source: IAudioPlayback; mySkin: TThemeEqualizer); procedure Draw; - - Procedure SetBands(Value: Byte); - Function GetBands: Byte; - Property Bands: Byte read GetBands write SetBands; + procedure SetBands(Value: byte); + function GetBands: byte; + property Bands: byte read GetBands write SetBands; procedure SetSource(newSource: IAudioPlayback); end; implementation -uses math, SDL, gl, glext; - +uses + math, + SDL, + gl, + glext; constructor Tms_Equalizer.Create(Source: IAudioPlayback; mySkin: TThemeEqualizer); -var I: Integer; +var + I: integer; begin - If (Source <> nil) then + if (Source <> nil) then begin - X := mySkin.X; - Y := mySkin.Y; - W := mySkin.W; - H := mySkin.H; - Z := mySkin.Z; + X := mySkin.X; + Y := mySkin.Y; + W := mySkin.W; + H := mySkin.H; + Z := mySkin.Z; - Space := mySkin.Space; + Space := mySkin.Space; - Visible := mySkin.Visible; - Alpha := mySkin.Alpha; - Color.R := mySkin.ColR; - Color.G := mySkin.ColG; - Color.B := mySkin.ColB; + Visible := mySkin.Visible; + Alpha := mySkin.Alpha; + Color.R := mySkin.ColR; + Color.G := mySkin.ColG; + Color.B := mySkin.ColB; - Direction := mySkin.Direction; - Bands := mySkin.Bands; - BandLength := mySkin.Length; + Direction := mySkin.Direction; + Bands := mySkin.Bands; + BandLength := mySkin.Length; Reflection := mySkin.Reflection; Reflectionspacing := mySkin.Reflectionspacing; @@ -116,31 +118,31 @@ begin //Check if Visible - If (Bands <= 0) OR - (BandLength <= 0) OR - (W <= 0) OR - (H <= 0) OR + if (Bands <= 0) or + (BandLength <= 0) or + (W <= 0) or + (H <= 0) or (Alpha <= 0) then - Visible := False; + Visible := false; //ClearArray - For I := low(BandData) to high(BandData) do + for I := low(BandData) to high(BandData) do BandData[I] := 3; end else - Visible := False; + Visible := false; end; //-------- // evaluate FFT-Data //-------- -Procedure Tms_Equalizer.Analyse; - var - I: Integer; - ChansPerBand: byte; // channels per band - MaxChannel: Integer; - Pos: Real; - CurBand: Integer; +procedure Tms_Equalizer.Analyse; +var + I: integer; + ChansPerBand: byte; // channels per band + MaxChannel: integer; + Pos: real; + CurBand: integer; begin Source.GetFFTData(FFTData); @@ -188,25 +190,26 @@ end; // Draw SpectrumAnalyser, Call Analyse //-------- procedure Tms_Equalizer.Draw; - var - CurTime: Cardinal; - PosX, PosY: Real; - I, J: Integer; - Diff: Real; +var + CurTime: cardinal; + PosX, PosY: real; + I, J: integer; + Diff: real; - Function GetAlpha(Diff: Single): Single; + function GetAlpha(Diff: single): single; begin - If Direction then - Result := (Alpha * 0.6) *(0.5 - Diff/(BandLength * (H + Space))) + if Direction then + Result := (Alpha * 0.6) * (0.5 - Diff/(BandLength * (H + Space))) else - Result := (Alpha * 0.6) *(0.5 - Diff/(Bands * (H + Space))); + Result := (Alpha * 0.6) * (0.5 - Diff/(Bands * (H + Space))); end; + begin - If (Visible) AND not (AudioPlayback.Finished) then + if (Visible) and not (AudioPlayback.Finished) then begin //Call Analyse if necessary CurTime := SDL_GetTicks(); - If (CurTime > RefreshTime) then + if (CurTime > RefreshTime) then begin Analyse; @@ -244,12 +247,12 @@ begin glVertex3f(PosX+W, PosY, Z); glEnd; - If (Reflection) AND (J <= BandLength div 2) then + if (Reflection) and (J <= BandLength div 2) then begin Diff := (Y-PosY) + H; //Draw Reflection - If Direction then + if Direction then begin glBegin(GL_QUADS); glColorRGB(Color, GetAlpha(Diff)); @@ -298,22 +301,20 @@ begin end; end; -Procedure Tms_Equalizer.SetBands(Value: Byte); +procedure Tms_Equalizer.SetBands(Value: byte); begin SetLength(BandData, Value); end; -Function Tms_Equalizer.GetBands: Byte; +function Tms_Equalizer.GetBands: byte; begin Result := Length(BandData); end; -Procedure Tms_Equalizer.SetSource(newSource: IAudioPlayback); +procedure Tms_Equalizer.SetSource(newSource: IAudioPlayback); begin - If (newSource <> nil) then + if (newSource <> nil) then Source := newSource; end; - - end.
\ No newline at end of file diff --git a/Lua/src/menu/UMenuInteract.pas b/Lua/src/menu/UMenuInteract.pas index 4c2d4e86..7cb92025 100644 --- a/Lua/src/menu/UMenuInteract.pas +++ b/Lua/src/menu/UMenuInteract.pas @@ -35,10 +35,19 @@ interface type TInteract = record // for moving thru menu - Typ: integer; // 0 - button, 1 - select, 2 - Text, 3 - Select SLide, 5 - ButtonCollection Child - Num: integer; // number of this item in proper list like buttons, selects + Typ: integer; // 0 - button, 1 - select, 2 - Text, 3 - Select SLide, 5 - ButtonCollection Child + Num: integer; // number of this item in proper list like buttons, selects end; + { to handle the area where the mouse is over a control } + TMouseOverRect = record + X, Y: Real; + W, H: Real; + end; + + { to handle the on click action } + TMouseClickAction = (maNone, maReturn, maLeft, maRight); + implementation end. diff --git a/Lua/src/menu/UMenuSelectSlide.pas b/Lua/src/menu/UMenuSelectSlide.pas index 1a0fa725..11be4c2a 100644 --- a/Lua/src/menu/UMenuSelectSlide.pas +++ b/Lua/src/menu/UMenuSelectSlide.pas @@ -34,10 +34,11 @@ interface {$I switches.inc} uses + gl, TextGL, + UMenuText, UTexture, - gl, - UMenuText; + UMenuInteract; type PSelectSlide = ^TSelectSlide; @@ -48,23 +49,29 @@ type // objects Text: TText; // Main text describing option TextOpt: array of TText; // 3 texts in the position of possible options - TextOptT: array of string; // array of names for possible options + TextOptT: array of UTF8String; // array of names for possible options Texture: TTexture; // Select Texture TextureSBG: TTexture; // Background Selections Texture // TextureS: array of TTexture; // Selections Texture (not used) -// TextureArrowL: TTexture; // Texture for left arrow (not used yet) -// TextureArrowR: TTexture; // Texture for right arrow (not used yet) + Tex_SelectS_ArrowL: TTexture; // Texture for left arrow + Tex_SelectS_ArrowR: TTexture; // Texture for right arrow SelectOptInt: integer; PData: ^integer; //For automatically Setting LineCount - Lines: Byte; + Lines: byte; + + //Arrows on/off + showArrows: boolean; //default is false + + //whether to show one item or all that fit into the select + oneItemOnly: boolean; //default is false //Visibility - Visible: Boolean; + Visible: boolean; // for selection and deselection // main static @@ -121,7 +128,7 @@ type // procedures procedure SetSelect(Value: boolean); - property Selected: Boolean read SelectBool write SetSelect; + property Selected: boolean read SelectBool write SetSelect; procedure SetSelectOpt(Value: integer); property SelectedOption: integer read SelectOptInt write SetSelectOpt; procedure Draw; @@ -129,10 +136,18 @@ type //Automatically Generate Lines (Texts) procedure genLines; + + function GetMouseOverArea: TMouseOverRect; + function OnClick(X, Y: Real): TMouseClickAction; end; implementation -uses UDrawTexture, math, ULog, SysUtils; + +uses + math, + SysUtils, + UDrawTexture, + ULog; // ------------ Select constructor TSelectSlide.Create; @@ -141,24 +156,17 @@ begin Text := TText.Create; SetLength(TextOpt, 1); TextOpt[0] := TText.Create; - - //Set Standard Width for Selections Background - SBGW := 450; - - Visible := True; - {SetLength(TextOpt, 3); - TextOpt[0] := TText.Create; - TextOpt[1] := TText.Create; - TextOpt[2] := TText.Create;} + Visible := true; end; procedure TSelectSlide.SetSelect(Value: boolean); {var - SO: integer; - I: integer;} + SO: integer; + I: integer;} begin SelectBool := Value; - if Value then begin + if Value then + begin Texture.ColR := ColR; Texture.ColG := ColG; Texture.ColB := ColB; @@ -173,15 +181,9 @@ begin TextureSBG.ColG := SBGColG; TextureSBG.ColB := SBGColB; TextureSBG.Int := SBGInt; - -{ for I := 0 to High(TextOpt) do begin - TextOpt[I].ColR := STColR; - TextOpt[I].ColG := STColG; - TextOpt[I].ColB := STColB; - TextOpt[I].Int := STInt; - end;} - - end else begin + end + else + begin Texture.ColR := DColR; Texture.ColG := DColG; Texture.ColB := DColB; @@ -196,185 +198,242 @@ begin TextureSBG.ColG := SBGDColG; TextureSBG.ColB := SBGDColB; TextureSBG.Int := SBGDInt; - -{ for I := 0 to High(TextOpt) do begin - TextOpt[I].ColR := STDColR; - TextOpt[I].ColG := STDColG; - TextOpt[I].ColB := STDColB; - TextOpt[I].Int := STDInt; - end;} end; end; procedure TSelectSlide.SetSelectOpt(Value: integer); var - SO: integer; - HalfL: integer; - HalfR: integer; + SO: integer; + HalfL: integer; + HalfR: integer; -procedure DoSelection(Sel: Cardinal); - var I: Integer; + procedure DoSelection(Sel: cardinal); + var + I: integer; begin - for I := low(TextOpt) to high(TextOpt) do + for I := Low(TextOpt) to High(TextOpt) do begin TextOpt[I].ColR := STDColR; TextOpt[I].ColG := STDColG; TextOpt[I].ColB := STDColB; TextOpt[I].Int := STDInt; end; - if (integer(Sel) <= high(TextOpt)) then + + if (integer(Sel) <= High(TextOpt)) then begin TextOpt[Sel].ColR := STColR; TextOpt[Sel].ColG := STColG; TextOpt[Sel].ColB := STColB; TextOpt[Sel].Int := STInt; + end; end; - end; + begin SelectOptInt := Value; PData^ := Value; -// SetSelect(true); // reset all colors - if (Length(TextOpt)>0) AND (Length(TextOptT)>0) then + if (Length(TextOpt) > 0) and (Length(TextOptT) > 0) then begin + //First option selected if (Value <= 0) then - begin //First Option Selected + begin Value := 0; - for SO := low (TextOpt) to high(TextOpt) do + Tex_SelectS_ArrowL.alpha := 0; + Tex_SelectS_ArrowR.alpha := 1; + + for SO := Low(TextOpt) to High(TextOpt) do begin - TextOpt[SO].Text := TextOptT[SO]; + TextOpt[SO].Text := TextOptT[SO]; end; DoSelection(0); end - else if (Value >= high(TextOptT)) then - begin //Last Option Selected - Value := high(TextOptT); - for SO := high(TextOpt) downto low (TextOpt) do + //Last option selected + else if (Value >= High(TextOptT)) then + begin + Value := High(TextOptT); + + Tex_SelectS_ArrowL.alpha := 1; + Tex_SelectS_ArrowR.alpha := 0; + + for SO := High(TextOpt) downto Low(TextOpt) do begin - TextOpt[SO].Text := TextOptT[high(TextOptT)-(Lines-SO-1)]; + TextOpt[SO].Text := TextOptT[High(TextOptT) - (Lines - SO - 1)]; end; - DoSelection(Lines-1); + DoSelection(Lines - 1); end + + //in between first and last else begin - HalfL := Ceil((Lines-1)/2); - HalfR := Lines-1-HalfL; - - if (Value <= HalfL) then - begin //Selected Option is near to the left side - {HalfL := Value; - HalfR := Lines-1-HalfL;} - //Change Texts - for SO := low (TextOpt) to high(TextOpt) do - begin - TextOpt[SO].Text := TextOptT[SO]; - end; + Tex_SelectS_ArrowL.alpha := 1; + Tex_SelectS_ArrowR.alpha := 1; - DoSelection(Value); - end - else if (Value > High(TextOptT)-HalfR) then - begin //Selected is too near to the right border - HalfR := high(TextOptT) - Value; - HalfL := Lines-1-HalfR; - //Change Texts - for SO := high(TextOpt) downto low (TextOpt) do + HalfL := Ceil((Lines - 1) / 2); + HalfR := Lines - 1 - HalfL; + + //Selected option is near to the left side + if (Value <= HalfL) then begin - TextOpt[SO].Text := TextOptT[high(TextOptT)-(Lines-SO-1)]; - end; + //Change texts + for SO := Low(TextOpt) to High(TextOpt) do + begin + TextOpt[SO].Text := TextOptT[SO]; + end; - DoSelection (HalfL); - end - else - begin - //Change Texts - for SO := low (TextOpt) to high(TextOpt) do + DoSelection(Value); + end + + //Selected option is near to the right side + else if (Value > High(TextOptT) - HalfR) then begin - TextOpt[SO].Text := TextOptT[Value - HalfL + SO]; - end; + HalfR := High(TextOptT) - Value; + HalfL := Lines - 1 - HalfR; + //Change texts + for SO := High(TextOpt) downto Low(TextOpt) do + begin + TextOpt[SO].Text := TextOptT[High(TextOptT) - (Lines - SO - 1)]; + end; - DoSelection(HalfL); - end; + DoSelection (HalfL); + end - end; + else + begin + //Change Texts + for SO := Low(TextOpt) to High(TextOpt) do + begin + TextOpt[SO].Text := TextOptT[Value - HalfL + SO]; + end; + DoSelection(HalfL); + end; + end; end; - end; procedure TSelectSlide.Draw; var - SO: integer; + SO: integer; begin if Visible then begin DrawTexture(Texture); DrawTexture(TextureSBG); + if showArrows then + begin + DrawTexture(Tex_SelectS_ArrowL); + DrawTexture(Tex_SelectS_ArrowR); + end; + Text.Draw; - for SO := low(TextOpt) to high(TextOpt) do + for SO := Low(TextOpt) to High(TextOpt) do TextOpt[SO].Draw; end; end; procedure TSelectSlide.GenLines; var -maxlength: Real; -I: Integer; + maxlength: real; + I: integer; begin SetFontStyle(0{Text.Style}); SetFontSize(Text.Size); maxlength := 0; - for I := low(TextOptT) to high (TextOptT) do + for I := Low(TextOptT) to High(TextOptT) do begin if (glTextWidth(TextOptT[I]) > maxlength) then maxlength := glTextWidth(TextOptT[I]); end; - Lines := floor((TextureSBG.W-40) / (maxlength+7)); - if (Lines > Length(TextOptT)) then - Lines := Length(TextOptT); - if (Lines <= 0) then + if (oneItemOnly = false) then + begin + //show all items + Lines := floor((TextureSBG.W-40) / (maxlength+7)); + if (Lines > Length(TextOptT)) then + Lines := Length(TextOptT); + + if (Lines <= 0) then + Lines := 1; + end + else + begin + //show one item only Lines := 1; + end; //Free old Space used by Texts - For I := low(TextOpt) to high(TextOpt) do + for I := Low(TextOpt) to High(TextOpt) do TextOpt[I].Free; setLength (TextOpt, Lines); - for I := low(TextOpt) to high(TextOpt) do + for I := Low(TextOpt) to High(TextOpt) do + begin + TextOpt[I] := TText.Create; + TextOpt[I].Size := Text.Size; + //TextOpt[I].Align := 1; + TextOpt[I].Align := 0; + TextOpt[I].Visible := true; + + TextOpt[I].ColR := STDColR; + TextOpt[I].ColG := STDColG; + TextOpt[I].ColB := STDColB; + TextOpt[I].Int := STDInt; + + //Generate Positions + //TextOpt[I].X := TextureSBG.X + 20 + (TextureSBG.W / Lines) * (I + 0.5); + if (I <> High(TextOpt)) or (High(TextOpt) = 0) or (Length(TextOptT) = Lines) then + TextOpt[I].X := TextureSBG.X + 20 + (TextureSBG.W / Lines) * I + else + TextOpt[I].X := TextureSBG.X + TextureSBG.W - maxlength; + + TextOpt[I].Y := TextureSBG.Y + (TextureSBG.H - Text.Size) / 2; + + //Better Look with 2 Options + if (Lines = 2) and (Length(TextOptT) = 2) then + TextOpt[I].X := TextureSBG.X + 20 + (TextureSBG.W -40 - glTextWidth(TextOptT[1])) * I; + + if (Lines = 1) then begin - TextOpt[I] := TText.Create; - TextOpt[I].Size := Text.Size; - //TextOpt[I].Align := 1; - TextOpt[I].Align := 0; - TextOpt[I].Visible := True; + TextOpt[I].Align := 1; //center text + TextOpt[I].X := TextureSBG.X + (TextureSBG.W / 2); + end; + end; +end; - TextOpt[I].ColR := STDColR; - TextOpt[I].ColG := STDColG; - TextOpt[I].ColB := STDColB; - TextOpt[I].Int := STDInt; +function TSelectSlide.GetMouseOverArea: TMouseOverRect; +begin + Result.X := Texture.X; + Result.Y := Texture.Y; + Result.W := (TextureSBG.X + TextureSBG.W) - Result.X; + Result.H := Max(Texture.H, TextureSBG.H); +end; - //Generate Positions - //TextOpt[I].X := TextureSBG.X + 20 + (TextureSBG.W / Lines) * (I + 0.5); - if (I <> High(TextOpt)) OR (High(TextOpt) = 0) OR (Length(TextOptT) = Lines) then - TextOpt[I].X := TextureSBG.X + 20 + (TextureSBG.W / Lines) * I - else - TextOpt[I].X := TextureSBG.X + TextureSBG.W - maxlength; +function TSelectSlide.OnClick(X, Y: Real): TMouseClickAction; + var + AreaW: Real; +begin + // default: press return on click + Result := maReturn; - TextOpt[I].Y := TextureSBG.Y + (TextureSBG.H - Text.Size) / 2; + // use left sides to inc or dec selection by click + AreaW := TextureSbg.W / 20; - //Better Look with 2 Options - if (Lines=2) AND (Length(TextOptT)= 2) then - TextOpt[I].X := TextureSBG.X + 20 + (TextureSBG.W -40 - glTextWidth(TextOptT[1])) * I; - end; + if (Y >= TextureSBG.Y) and (Y <= TextureSBG.Y + TextureSBG.H) then + begin + if (X >= TextureSBG.X) and (X <= TextureSBG.X + AreaW) then + Result := maLeft // hit left area + else if (X >= TextureSBG.X + TextureSBG.W - AreaW) and (X <= TextureSBG.X + TextureSBG.W) then + Result := maRight; // hit right area + end; end; end. diff --git a/Lua/src/menu/UMenuStatic.pas b/Lua/src/menu/UMenuStatic.pas index 9a10fade..72f4eb36 100644 --- a/Lua/src/menu/UMenuStatic.pas +++ b/Lua/src/menu/UMenuStatic.pas @@ -40,19 +40,20 @@ uses type TStatic = class public - Texture: TTexture; // Button Screen position and size - Visible: boolean; + Texture: TTexture; // Button Screen position and size + Visible: boolean; //Reflection Mod - Reflection: boolean; - Reflectionspacing: Real; + Reflection: boolean; + Reflectionspacing: real; procedure Draw; constructor Create(Textura: TTexture); overload; end; implementation -uses UDrawTexture; +uses + UDrawTexture; procedure TStatic.Draw; begin diff --git a/Lua/src/menu/UMenuText.pas b/Lua/src/menu/UMenuText.pas index a3d13834..276f961b 100644 --- a/Lua/src/menu/UMenuText.pas +++ b/Lua/src/menu/UMenuText.pas @@ -34,29 +34,29 @@ interface {$I switches.inc} uses - TextGL, - UTexture, - gl, math, SysUtils, - SDL; + gl, + SDL, + TextGL, + UTexture; type TText = class private - SelectBool: boolean; - TextString: string; - TextTiles: array of string; + SelectBool: boolean; + TextString: UTF8String; + TextTiles: array of UTF8String; - STicks: Cardinal; - SelectBlink: boolean; + STicks: cardinal; + SelectBlink: boolean; public X: real; Y: real; Z: real; - MoveX: real; //Some Modifier for X - Position that don't affect the real Y - MoveY: real; //Some Modifier for Y - Position that don't affect the real Y - W: real; //text wider than W is broken + MoveX: real; // some modifier for x - position that don't affect the real Y + MoveY: real; // some modifier for y - position that don't affect the real Y + W: real; // text wider than W is broken // H: real; Size: real; ColR: real; @@ -64,66 +64,69 @@ type ColB: real; Alpha: real; Int: real; - Style: integer; - Visible: boolean; - Align: integer; // 0 = left, 1 = center, 2 = right + Style: integer; + Visible: boolean; + Align: integer; // 0 = left, 1 = center, 2 = right - //Reflection - Reflection: boolean; - ReflectionSpacing: real; + // reflection + Reflection: boolean; + ReflectionSpacing: real; procedure SetSelect(Value: boolean); property Selected: boolean read SelectBool write SetSelect; - procedure SetText(Value: string); - property Text: string read TextString write SetText; + procedure SetText(Value: UTF8String); + property Text: UTF8String read TextString write SetText; - procedure DeleteLastL; //Procedure to Delete Last Letter + procedure DeleteLastLetter; //< Deletes the rightmost letter procedure Draw; constructor Create; overload; - constructor Create(X, Y: real; Text: string); overload; - constructor Create(ParX, ParY, ParW: real; ParStyle: integer; ParSize, ParColR, ParColG, ParColB: real; ParAlign: integer; ParText: string; ParReflection: boolean; ParReflectionSpacing: real; ParZ: real); overload; + constructor Create(X, Y: real; const Text: UTF8String); overload; + constructor Create(ParX, ParY, ParW: real; ParStyle: integer; ParSize, ParColR, ParColG, ParColB: real; ParAlign: integer; const ParText: UTF8String; ParReflection: boolean; ParReflectionSpacing: real; ParZ: real); overload; end; implementation -uses UGraphic, - StrUtils; +uses + UGraphic, + UUnicodeUtils, + StrUtils; procedure TText.SetSelect(Value: boolean); begin SelectBool := Value; - //Set Cursor Visible - SelectBlink := True; + // set cursor visible + SelectBlink := true; STicks := SDL_GetTicks() div 550; end; -procedure TText.SetText(Value: string); +procedure TText.SetText(Value: UTF8String); var - NextPos: Cardinal; //NextPos of a Space etc. - LastPos: Cardinal; //LastPos " - LastBreak: Cardinal; //Last Break - isBreak: boolean; //True if the Break is not Caused because the Text is out of the area - FirstWord: Word; //Is First Word after Break? - Len: Word; //Length of the Tiles Array + NextPos: cardinal; // next pos of a space etc. + LastPos: cardinal; // last pos " + LastBreak: cardinal; // last break + isBreak: boolean; // true if the break is not caused because the text is out of the area + FirstWord: word; // is first word after break? + Len: word; // length of the tiles array function GetNextPos: boolean; var - T1, {T2,} T3: Cardinal; + T1, {T2,} T3: cardinal; begin LastPos := NextPos; - //Next Space (If Width is given) + // next space (if width is given) if (W > 0) then T1 := PosEx(' ', Value, LastPos + 1) - else T1 := Length(Value); + else + T1 := Length(Value); - {//Next - + {// next - T2 := PosEx('-', Value, LastPos + 1);} - //Next Break + // next break T3 := PosEx('\n', Value, LastPos + 1); if T1 = 0 then @@ -133,19 +136,19 @@ var if T3 = 0 then T3 := Length(Value); - //Get Nearest Pos + // get nearest pos NextPos := min(T1, T3{min(T2, T3)}); - if (LastPos = Length(Value)) then + if (LastPos = cardinal(Length(Value))) then NextPos := 0; - isBreak := (NextPos = T3) AND (NextPos <> Length(Value)); + isBreak := (NextPos = T3) and (NextPos <> cardinal(Length(Value))); Result := (NextPos <> 0); end; - procedure AddBreak(const From, bTo: Cardinal); + procedure AddBreak(const From, bTo: cardinal); begin - if (isBreak) OR (bTo - From >= 1) then + if (isBreak) or (bTo - From >= 1) then begin Inc(Len); SetLength (TextTiles, Len); @@ -160,14 +163,14 @@ var end; begin - //Set TExtstring + // set TextString TextString := Value; - //Set Cursor Visible - SelectBlink := True; + // set cursor visible + SelectBlink := true; STicks := SDL_GetTicks() div 550; - //Exit if there is no Need to Create Tiles + // exit if there is no need to create tiles if (W <= 0) and (Pos('\n', Value) = 0) then begin SetLength (TextTiles, 1); @@ -175,12 +178,12 @@ begin Exit; end; - //Create Tiles - //Reset Text Array + // create tiles + // reset text array SetLength (TextTiles, 0); Len := 0; - //Reset Counter Vars + // reset counter vars LastPos := 1; NextPos := 1; LastBreak := 1; @@ -188,105 +191,98 @@ begin if (W > 0) then begin - //Set Font Properties + // set font properties SetFontStyle(Style); SetFontSize(Size); end; - //go Through Text + // go through text while (GetNextPos) do begin - //Break in Text + // break in text if isBreak then begin - //Look for Break before the Break + // look for break before the break if (glTextWidth(Copy(Value, LastBreak, NextPos - LastBreak + 1)) > W) AND (NextPos-LastPos > 1) then begin - isBreak := False; - //Not the First word after Break, so we don't have to break within a word + isBreak := false; + // not the first word after break, so we don't have to break within a word if (FirstWord > 1) then begin - //Add Break before actual Position, because there the Text fits the Area + // add break before actual position, because there the text fits the area AddBreak(LastBreak, LastPos); end - else //First Word after Break Break within the Word + else // first word after break break within the word begin - //ToDo - //AddBreak(LastBreak, LastBreak + 155); + // to do + // AddBreak(LastBreak, LastBreak + 155); end; end; - isBreak := True; - //Add Break from Text + isBreak := true; + // add break from text AddBreak(LastBreak, NextPos); end - //Text comes out of the Text Area -> CreateBreak + // text comes out of the text area -> createbreak else if (glTextWidth(Copy(Value, LastBreak, NextPos - LastBreak + 1)) > W) then begin - //Not the First word after Break, so we don't have to break within a word + // not the first word after break, so we don't have to break within a word if (FirstWord > 1) then begin - //Add Break before actual Position, because there the Text fits the Area + // add break before actual position, because there the text fits the area AddBreak(LastBreak, LastPos); end - else //First Word after Break -> Break within the Word + else // first word after break -> break within the word begin - //ToDo - //AddBreak(LastBreak, LastBreak + 155); + // to do + // AddBreak(LastBreak, LastBreak + 155); end; end; //end; Inc(FirstWord) end; - //Add Ending + // add ending AddBreak(LastBreak, Length(Value)+1); end; -procedure TText.DeleteLastL; -var - S: string; - L: integer; +procedure TText.DeleteLastLetter; begin - S := TextString; - L := Length(S); - if (L > 0) then - SetLength(S, L-1); - - SetText(S); + SetText(UTF8Copy(TextString, 1, LengthUTF8(TextString)-1)); end; procedure TText.Draw; var X2, Y2: real; - Text2: string; + Text2: UTF8String; I: integer; + Ticks: cardinal; begin if Visible then begin SetFontStyle(Style); SetFontSize(Size); - SetFontItalic(False); + SetFontItalic(false); glColor4f(ColR*Int, ColG*Int, ColB*Int, Alpha); - //Reflection - if Reflection = true then + // reflection + if Reflection then SetFontReflection(true, ReflectionSpacing) else SetFontReflection(false,0); - //if selected set blink... + // if selected set blink... if SelectBool then begin - I := SDL_GetTicks() div 550; - if I <> STicks then - begin //Change Visability - STicks := I; + Ticks := SDL_GetTicks() div 550; + if Ticks <> STicks then + begin // change visability + STicks := Ticks; SelectBlink := Not SelectBlink; end; end; - {if (False) then //no width set draw as one long string + {if (false) then // no width set draw as one long string begin if not (SelectBool AND SelectBlink) then Text2 := Text @@ -305,20 +301,20 @@ begin end else begin} - //now use allways: - //draw text as many strings + // now use always: + // draw text as many strings Y2 := Y + MoveY; - for I := 0 to high(TextTiles) do + for I := 0 to High(TextTiles) do begin - if (not (SelectBool and SelectBlink)) or (I <> high(TextTiles)) then + if (not (SelectBool and SelectBlink)) or (I <> High(TextTiles)) then Text2 := TextTiles[I] else Text2 := TextTiles[I] + '|'; case Align of - 0: X2 := X + MoveX; - 1: X2 := X + MoveX - glTextWidth(Text2)/2; - 2: X2 := X + MoveX - glTextWidth(Text2); + 1: X2 := X + MoveX - glTextWidth(Text2)/2; { centered } + 2: X2 := X + MoveX - glTextWidth(Text2); { right aligned } + else X2 := X + MoveX; { left aligned (default) } end; SetFontPos(X2, Y2); @@ -346,12 +342,19 @@ begin Create(0, 0, ''); end; -constructor TText.Create(X, Y: real; Text: string); +constructor TText.Create(X, Y: real; const Text: UTF8String); begin Create(X, Y, 0, 0, 30, 0, 0, 0, 0, Text, false, 0, 0); end; -constructor TText.Create(ParX, ParY, ParW: real; ParStyle: integer; ParSize, ParColR, ParColG, ParColB: real; ParAlign: integer; ParText: string; ParReflection: boolean; ParReflectionSpacing: real; ParZ:real); +constructor TText.Create(ParX, ParY, ParW: real; + ParStyle: integer; + ParSize, ParColR, ParColG, ParColB: real; + ParAlign: integer; + const ParText: UTF8String; + ParReflection: boolean; + ParReflectionSpacing: real; + ParZ: real); begin inherited Create; Alpha := 1; @@ -369,8 +372,8 @@ begin Align := ParAlign; SelectBool := false; Visible := true; - Reflection:= ParReflection; - ReflectionSpacing:= ParReflectionSpacing; + Reflection := ParReflection; + ReflectionSpacing := ParReflectionSpacing; end; end. diff --git a/Lua/src/screens/UScreenCredits.pas b/Lua/src/screens/UScreenCredits.pas index 7e85c5d4..b1333b4a 100644 --- a/Lua/src/screens/UScreenCredits.pas +++ b/Lua/src/screens/UScreenCredits.pas @@ -34,30 +34,31 @@ interface {$I switches.inc} uses - UMenu, - SDL, - SDL_Image, - UDisplay, - UTexture, - gl, - UMusic, - UFiles, - SysUtils, - UThemes, - UGraphicClasses; + SysUtils, + SDL, + SDL_Image, + gl, + UMenu, + UDisplay, + UTexture, + UMusic, + UFiles, + UThemes, + UPath, + UGraphicClasses; type - TCreditsStages=(InitialDelay,Intro,MainPart,Outro); + TCreditsStages=(InitialDelay, Intro, MainPart, Outro); TScreenCredits = class(TMenu) public - Credits_X: Real; - Credits_Time: Cardinal; - Credits_Alpha: Cardinal; - CTime: Cardinal; - CTime_hold: Cardinal; - ESC_Alpha: Integer; + Credits_X: real; + Credits_Time: cardinal; + Credits_Alpha: cardinal; + CTime: cardinal; + CTime_hold: cardinal; + ESC_Alpha: integer; credits_entry: TTexture; credits_entry_dx: TTexture; @@ -89,31 +90,31 @@ type deluxe_slidein: cardinal; - CurrentScrollText: String; - NextScrollUpdate: Real; - EndofLastScrollingPart: Cardinal; - CurrentScrollStart, CurrentScrollEnd: Integer; + CurrentScrollText: string; + NextScrollUpdate: real; + EndofLastScrollingPart: cardinal; + CurrentScrollStart, CurrentScrollEnd: integer; CRDTS_Stage: TCreditsStages; - Fadeout: boolean; + Fadeout: boolean; constructor Create; override; - function ParseInput(PressedKey: Cardinal; CharCode: WideChar; PressedDown: Boolean): Boolean; override; + function ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; override; function Draw: boolean; override; - procedure onShow; override; - procedure onHide; override; + procedure OnShow; override; + procedure OnHide; override; procedure DrawCredits; procedure Draw_FunkyText; end; const Funky_Text: string = - 'Grandma Deluxe has arrived! Thanks to Corvus5 for the massive work on UltraStar, Wome for the nice tune you´re hearing, '+ - 'all the people who put massive effort and work in new songs (don´t forget UltraStar w/o songs would be nothing), ppl from '+ + 'Grandma Deluxe has arrived! Thanks to Corvus5 for the massive work on UltraStar, Wome for the nice tune you are hearing, '+ + 'all the people who put massive effort and work in new songs (do not forget UltraStar w/o songs would be nothing), ppl from '+ 'irc helping us - eBandit and Gabari, scene ppl who really helped instead of compiling and running away. Greetings to DennisTheMenace for betatesting, '+ 'Demoscene.tv, pouet.net, KakiArts, Sourceforge,..'; - CRDTS_BG_FILE = 'credits_v5_bg.png'; + CRDTS_BG_FILE = 'credits_v5_bg.png'; CRDTS_OVL_FILE = 'credits_v5_overlay.png'; CRDTS_blindguard_FILE = 'names_blindguard.png'; CRDTS_blindy_FILE = 'names_blindy.png'; @@ -137,18 +138,18 @@ const OUTRO_ESC_FILE = 'outro-esc.png'; OUTRO_EXD_FILE = 'outro-exit-dark.png'; - Timings: array[0..21] of Cardinal=( - 20, // 0 Delay vor Start + Timings: array[0..21] of cardinal=( + 20, // 0 Delay before Start - 149, // 1 Ende erster Intro Zoom - 155, // 2 Start 2. Action im Intro - 170, // 3 Ende Separation im Intro - 271, // 4 Anfang Zoomout im Intro + 149, // 1 End first Intro Zoom + 155, // 2 Start 2. Action in Intro + 170, // 3 End Separation in Intro + 271, // 4 beginning Zoomout in Intro 0, // 5 unused - 261, // 6 Start fade-to-white im Intro + 261, // 6 Start fade-to-white in Intro 271, // 7 Start Main Part - 280, // 8 Start On-Beat-Sternchen Main Part + 280, // 8 Start On-Beat-Star Main Part 396, // 9 Start BlindGuard 666, // 10 Start blindy @@ -162,26 +163,26 @@ const 2826, // 18 Ende Whiteshark 3096, // 19 Start FadeOut Mainscreen 3366, // 20 Ende Credits Tune - 60); // 21 start flare im intro + 60); // 21 start flare in intro implementation uses - ULog, - UGraphic, - UMain, - UIni, - USongs, - Textgl, - ULanguage, - UCommon, - Math; - - -function TScreenCredits.ParseInput(PressedKey: Cardinal; CharCode: WideChar; PressedDown: Boolean): Boolean; + Math, + ULog, + UGraphic, + UMain, + UIni, + USongs, + Textgl, + ULanguage, + UCommon, + UPathUtils; + +function TScreenCredits.ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; begin Result := true; - If (PressedDown) Then + if (PressedDown) then begin // Key Down case PressedKey of @@ -191,51 +192,51 @@ begin FadeTo(@ScreenMain); AudioPlayback.PlaySound(SoundLib.Back); end; - { +{ SDLK_SPACE: begin setlength(CTime_hold,length(CTime_hold)+1); CTime_hold[high(CTime_hold)]:=CTime; end; - } - end;//esac - end; //fi +} + end; // esac + end; // fi end; constructor TScreenCredits.Create; var - CreditsPath: string; + CreditsPath: IPath; begin inherited Create; - CreditsPath := ResourcesPath + 'credits/'; - - credits_bg_tex := Texture.LoadTexture(CreditsPath + CRDTS_BG_FILE, TEXTURE_TYPE_PLAIN, 0); - credits_bg_ovl := Texture.LoadTexture(CreditsPath + CRDTS_OVL_FILE, TEXTURE_TYPE_TRANSPARENT, 0); - - credits_blindguard := Texture.LoadTexture(CreditsPath + CRDTS_blindguard_FILE, TEXTURE_TYPE_TRANSPARENT, 0); - credits_blindy := Texture.LoadTexture(CreditsPath + CRDTS_blindy_FILE, TEXTURE_TYPE_TRANSPARENT, 0); - credits_canni := Texture.LoadTexture(CreditsPath + CRDTS_canni_FILE, TEXTURE_TYPE_TRANSPARENT, 0); - credits_commandio := Texture.LoadTexture(CreditsPath + CRDTS_commandio_FILE, TEXTURE_TYPE_TRANSPARENT, 0); - credits_lazyjoker := Texture.LoadTexture(CreditsPath + CRDTS_lazyjoker_FILE, TEXTURE_TYPE_TRANSPARENT, 0); - credits_mog := Texture.LoadTexture(CreditsPath + CRDTS_mog_FILE, TEXTURE_TYPE_TRANSPARENT, 0); - credits_mota := Texture.LoadTexture(CreditsPath + CRDTS_mota_FILE, TEXTURE_TYPE_TRANSPARENT, 0); - credits_skillmaster := Texture.LoadTexture(CreditsPath + CRDTS_skillmaster_FILE, TEXTURE_TYPE_TRANSPARENT, 0); - credits_whiteshark := Texture.LoadTexture(CreditsPath + CRDTS_whiteshark_FILE, TEXTURE_TYPE_TRANSPARENT, 0); - - intro_layer01 := Texture.LoadTexture(CreditsPath + INTRO_L01_FILE, TEXTURE_TYPE_TRANSPARENT, 0); - intro_layer02 := Texture.LoadTexture(CreditsPath + INTRO_L02_FILE, TEXTURE_TYPE_TRANSPARENT, 0); - intro_layer03 := Texture.LoadTexture(CreditsPath + INTRO_L03_FILE, TEXTURE_TYPE_TRANSPARENT, 0); - intro_layer04 := Texture.LoadTexture(CreditsPath + INTRO_L04_FILE, TEXTURE_TYPE_TRANSPARENT, 0); - intro_layer05 := Texture.LoadTexture(CreditsPath + INTRO_L05_FILE, TEXTURE_TYPE_TRANSPARENT, 0); - intro_layer06 := Texture.LoadTexture(CreditsPath + INTRO_L06_FILE, TEXTURE_TYPE_TRANSPARENT, 0); - intro_layer07 := Texture.LoadTexture(CreditsPath + INTRO_L07_FILE, TEXTURE_TYPE_TRANSPARENT, 0); - intro_layer08 := Texture.LoadTexture(CreditsPath + INTRO_L08_FILE, TEXTURE_TYPE_TRANSPARENT, 0); - intro_layer09 := Texture.LoadTexture(CreditsPath + INTRO_L09_FILE, TEXTURE_TYPE_TRANSPARENT, 0); - - outro_bg := Texture.LoadTexture(CreditsPath + OUTRO_BG_FILE, TEXTURE_TYPE_PLAIN, 0); - outro_esc := Texture.LoadTexture(CreditsPath + OUTRO_ESC_FILE, TEXTURE_TYPE_TRANSPARENT, 0); - outro_exd := Texture.LoadTexture(CreditsPath + OUTRO_EXD_FILE, TEXTURE_TYPE_TRANSPARENT, 0); + CreditsPath := ResourcesPath.Append('credits', pdAppend); + + credits_bg_tex := Texture.LoadTexture(CreditsPath.Append(CRDTS_BG_FILE), TEXTURE_TYPE_PLAIN, 0); + credits_bg_ovl := Texture.LoadTexture(CreditsPath.Append(CRDTS_OVL_FILE), TEXTURE_TYPE_TRANSPARENT, 0); + + credits_blindguard := Texture.LoadTexture(CreditsPath.Append(CRDTS_blindguard_FILE), TEXTURE_TYPE_TRANSPARENT, 0); + credits_blindy := Texture.LoadTexture(CreditsPath.Append(CRDTS_blindy_FILE), TEXTURE_TYPE_TRANSPARENT, 0); + credits_canni := Texture.LoadTexture(CreditsPath.Append(CRDTS_canni_FILE), TEXTURE_TYPE_TRANSPARENT, 0); + credits_commandio := Texture.LoadTexture(CreditsPath.Append(CRDTS_commandio_FILE), TEXTURE_TYPE_TRANSPARENT, 0); + credits_lazyjoker := Texture.LoadTexture(CreditsPath.Append(CRDTS_lazyjoker_FILE), TEXTURE_TYPE_TRANSPARENT, 0); + credits_mog := Texture.LoadTexture(CreditsPath.Append(CRDTS_mog_FILE), TEXTURE_TYPE_TRANSPARENT, 0); + credits_mota := Texture.LoadTexture(CreditsPath.Append(CRDTS_mota_FILE), TEXTURE_TYPE_TRANSPARENT, 0); + credits_skillmaster := Texture.LoadTexture(CreditsPath.Append(CRDTS_skillmaster_FILE), TEXTURE_TYPE_TRANSPARENT, 0); + credits_whiteshark := Texture.LoadTexture(CreditsPath.Append(CRDTS_whiteshark_FILE), TEXTURE_TYPE_TRANSPARENT, 0); + + intro_layer01 := Texture.LoadTexture(CreditsPath.Append(INTRO_L01_FILE), TEXTURE_TYPE_TRANSPARENT, 0); + intro_layer02 := Texture.LoadTexture(CreditsPath.Append(INTRO_L02_FILE), TEXTURE_TYPE_TRANSPARENT, 0); + intro_layer03 := Texture.LoadTexture(CreditsPath.Append(INTRO_L03_FILE), TEXTURE_TYPE_TRANSPARENT, 0); + intro_layer04 := Texture.LoadTexture(CreditsPath.Append(INTRO_L04_FILE), TEXTURE_TYPE_TRANSPARENT, 0); + intro_layer05 := Texture.LoadTexture(CreditsPath.Append(INTRO_L05_FILE), TEXTURE_TYPE_TRANSPARENT, 0); + intro_layer06 := Texture.LoadTexture(CreditsPath.Append(INTRO_L06_FILE), TEXTURE_TYPE_TRANSPARENT, 0); + intro_layer07 := Texture.LoadTexture(CreditsPath.Append(INTRO_L07_FILE), TEXTURE_TYPE_TRANSPARENT, 0); + intro_layer08 := Texture.LoadTexture(CreditsPath.Append(INTRO_L08_FILE), TEXTURE_TYPE_TRANSPARENT, 0); + intro_layer09 := Texture.LoadTexture(CreditsPath.Append(INTRO_L09_FILE), TEXTURE_TYPE_TRANSPARENT, 0); + + outro_bg := Texture.LoadTexture(CreditsPath.Append(OUTRO_BG_FILE), TEXTURE_TYPE_PLAIN, 0); + outro_esc := Texture.LoadTexture(CreditsPath.Append(OUTRO_ESC_FILE), TEXTURE_TYPE_TRANSPARENT, 0); + outro_exd := Texture.LoadTexture(CreditsPath.Append(OUTRO_EXD_FILE), TEXTURE_TYPE_TRANSPARENT, 0); CRDTS_Stage:=InitialDelay; end; @@ -243,99 +244,42 @@ end; function TScreenCredits.Draw: boolean; begin DrawCredits; - Draw:=true; + Draw := true; end; -function pixfmt_eq(fmt1,fmt2: TSDL_Pixelformat): boolean; -begin - if (fmt1.BitsPerPixel = fmt2.BitsPerPixel) and - (fmt1.BytesPerPixel = fmt2.BytesPerPixel) and - (fmt1.Rloss = fmt2.Rloss) and - (fmt1.Gloss = fmt2.Gloss) and - (fmt1.Bloss = fmt2.Bloss) and - (fmt1.Rmask = fmt2.Rmask) and - (fmt1.Gmask = fmt2.Gmask) and - (fmt1.Bmask = fmt2.Bmask) and - (fmt1.Rshift = fmt2.Rshift) and - (fmt1.Gshift = fmt2.Gshift) and - (fmt1.Bshift = fmt2.Bshift) - then - pixfmt_eq:=True - else - pixfmt_eq:=False; -end; - -function inttohexstr(i: cardinal):pchar; -var helper, i2, c:cardinal; - tmpstr: string; -begin - helper:=0; - i2:=i; - tmpstr:=''; - for c:=1 to 8 do - begin - helper:=(helper shl 4) or (i2 and $f); - i2:=i2 shr 4; - end; - for c:=1 to 8 do - begin - i2:=helper and $f; - helper := helper shr 4; - case i2 of - 0: tmpstr:=tmpstr+'0'; - 1: tmpstr:=tmpstr+'1'; - 2: tmpstr:=tmpstr+'2'; - 3: tmpstr:=tmpstr+'3'; - 4: tmpstr:=tmpstr+'4'; - 5: tmpstr:=tmpstr+'5'; - 6: tmpstr:=tmpstr+'6'; - 7: tmpstr:=tmpstr+'7'; - 8: tmpstr:=tmpstr+'8'; - 9: tmpstr:=tmpstr+'9'; - 10: tmpstr:=tmpstr+'a'; - 11: tmpstr:=tmpstr+'b'; - 12: tmpstr:=tmpstr+'c'; - 13: tmpstr:=tmpstr+'d'; - 14: tmpstr:=tmpstr+'e'; - 15: tmpstr:=tmpstr+'f'; - end; - end; - inttohexstr:=pchar(tmpstr); -end; - -procedure TScreenCredits.onShow; +procedure TScreenCredits.OnShow; begin inherited; - CRDTS_Stage:=InitialDelay; - Credits_X := 580; + CRDTS_Stage := InitialDelay; + Credits_X := 580; deluxe_slidein := 0; - Credits_Alpha := 0; - //Music.SetLoop(true); loop loops not, shit - AudioPlayback.Open(soundpath + 'wome-credits-tune.mp3'); // thank you wetue - //Music.Play; - CTime:=0; - //setlength(CTime_hold,0); + Credits_Alpha := 0; +// Music.SetLoop(true); loop loops not, shit + AudioPlayback.Open(soundpath.Append('wome-credits-tune.mp3')); // thank you wetue +// Music.Play; + CTime := 0; +// setlength(CTime_hold,0); end; -procedure TScreenCredits.onHide; +procedure TScreenCredits.OnHide; begin AudioPlayback.Stop; end; Procedure TScreenCredits.Draw_FunkyText; var - S: Integer; - X,Y,A: Real; + S: integer; + X, Y, A: real; visibleText: string; begin SetFontSize(30); - //Init ScrollingText + // init ScrollingText if (CTime = Timings[7]) then begin - //Set Position of Text - Credits_X := 600; + // set position of text + Credits_X := 600; CurrentScrollStart := 1; CurrentScrollEnd := 1; end; @@ -348,8 +292,8 @@ begin for S := 1 to length(visibleText) do begin - Y := abs(sin((Credits_X+X)*0.93{*(((Credits_X+X))/1200)}/100*pi)); - SetFontPos(Credits_X+X, 538-Y*(Credits_X+X)*(Credits_X+X)*(Credits_X+X)/1000000); + Y := abs(sin((Credits_X + X) * 0.93 { * (((Credits_X + X)) / 1200) } / 100 * pi)); + SetFontPos(Credits_X + X, 538 - Y * (Credits_X + X) * (Credits_X + X) * (Credits_X + X) / 1000000); if (Credits_X + X > 32) then A := 17 @@ -358,10 +302,10 @@ begin else A := 0; - glColor4f(230/255-40/255+Y*(Credits_X+X)/900, - 200/255-30/255+Y*(Credits_X+X)/1000, - 155/255-20/255+Y*(Credits_X+X)/1100, - A/17); + glColor4f(230 / 255 - 40 / 255 + Y * (Credits_X + X)/ 900, + 200 / 255 - 30 / 255 + Y * (Credits_X + X)/ 1000, + 155 / 255 - 20 / 255 + Y * (Credits_X + X)/ 1100, + A / 17); glPrint(visibleText[S]); X := X + glTextWidth(visibleText[S]); end; @@ -380,50 +324,53 @@ begin inc(CurrentScrollEnd); end; end; - { // timing hack - X:=5; - SetFontStyle (2); - SetFontItalic(False); - SetFontSize(27); - glColor4f(1, 1, 1, 1); - for S:=0 to high(CTime_hold) do begin - visibleText:=inttostr(CTime_hold[S]); - SetFontPos (500, X); - glPrint (visibleText[0]); - X:=X+20; - end; - } +{ +// timing hack + X:=5; + SetFontStyle(2); + SetFontItalic(false); + SetFontSize(27); + glColor4f(1, 1, 1, 1); + for S := 0 to high(CTime_hold) do + begin + visibleText := inttostr(CTime_hold[S]); + SetFontPos (500, X); + glPrint(visibleText[0]); + X := X + 20; + end; +} end; procedure Start3D; begin - glMatrixMode(GL_PROJECTION); - glPushMatrix; - glLoadIdentity; - glFrustum(-0.3*4/3,0.3*4/3,-0.3,0.3,1,1000); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity; + glMatrixMode(GL_PROJECTION); + glPushMatrix; + glLoadIdentity; + glFrustum(-0.3 * 4 / 3, 0.3 * 4 / 3, -0.3, 0.3, 1, 1000); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity; end; + procedure End3D; begin - glMatrixMode(GL_PROJECTION); - glPopMatrix; - glMatrixMode(GL_MODELVIEW); + glMatrixMode(GL_PROJECTION); + glPopMatrix; + glMatrixMode(GL_MODELVIEW); end; procedure TScreenCredits.DrawCredits; var - T: Cardinal; - Data: TFFTData; - j,k,l:cardinal; - f,g: Real; - STime:cardinal; - Delay:cardinal; - myScale: Real; - myAngle: Real; + T: cardinal; + Data: TFFTData; + j, k, l: cardinal; + f, g: real; + STime: cardinal; + Delay: cardinal; + myScale: real; + myAngle: real; const - myLogoCoords: Array[0..27,0..1] of Cardinal = ( - (39,32),(84,32),(100,16),(125,24), + myLogoCoords: array[0..27,0..1] of cardinal = ( + ( 39,32),( 84,32),(100,16),(125,24), (154,31),(156,58),(168,32),(203,36), (258,34),(251,50),(274,93),(294,84), (232,54),(278,62),(319,34),(336,92), @@ -432,10 +379,10 @@ const (450,32),(485,34),(444,91),(486,93) ); begin - //dis does teh muiwk y0r + // dis does teh muiwk y0r to be translated :-) AudioPlayback.GetFFTData(Data); - Log.LogStatus('',' JB-1'); + Log.LogStatus('', ' JB-1'); T := SDL_GetTicks() div 33; if T <> Credits_Time then @@ -445,165 +392,172 @@ begin inc(CTime_hold); Credits_X := Credits_X-2; - Log.LogStatus('',' JB-2'); - if (CRDTS_Stage=InitialDelay) and (CTime=Timings[0]) then + Log.LogStatus('', ' JB-2'); + if (CRDTS_Stage=InitialDelay) and (CTime = Timings[0]) then begin - //CTime:=Timings[20]; - //CRDTS_Stage:=Outro; - CRDTS_Stage:=Intro; - CTime:=0; +// CTime := Timings[20]; +// CRDTS_Stage := Outro; + CRDTS_Stage := Intro; + CTime := 0; AudioPlayback.Play; end; - if (CRDTS_Stage=Intro) and (CTime=Timings[7]) then + if (CRDTS_Stage = Intro) and (CTime = Timings[7]) then begin - CRDTS_Stage:=MainPart; + CRDTS_Stage := MainPart; end; - if (CRDTS_Stage=MainPart) and (CTime=Timings[20]) then + if (CRDTS_Stage = MainPart) and (CTime = Timings[20]) then begin - CRDTS_Stage:=Outro; + CRDTS_Stage := Outro; end; end; - Log.LogStatus('',' JB-3'); + Log.LogStatus('', ' JB-3'); - //draw background - if CRDTS_Stage=InitialDelay then - begin - glClearColor(0,0,0,0); - glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT); - end + // draw background + if CRDTS_Stage = InitialDelay then + begin + glClearColor(0, 0, 0, 0); + glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT); + end else - if CRDTS_Stage=Intro then - begin - Start3D; - glPushMatrix; - - glClearColor(0,0,0,0); - glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT); + if CRDTS_Stage = Intro then + begin + Start3D; + glPushMatrix; - glEnable(GL_TEXTURE_2D); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glEnable(GL_BLEND); + glClearColor(0, 0, 0, 0); + glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT); - if CTime < Timings[1] then begin - myScale:= 0.5+0.5*(Timings[1]-CTime)/(Timings[1]); // slowly move layers together - myAngle:=cos((CTime)*pi/((Timings[1])*2)); // and make logo face towards camera - end else begin // this is the part when the logo stands still - myScale:=0.5; - myAngle:=0; - end; - if CTime > Timings[2] then begin - myScale:= 0.5+0.5*(CTime-Timings[2])/(Timings[3]-Timings[2]); // get some space between layers - myAngle:=0; - end; - //if CTime > Timings[3] then myScale:=1; // keep the space between layers - glTranslatef(0,0,-5+0.5*myScale); - if CTime > Timings[3] then myScale:=1; // keep the space between layers - if CTime > Timings[3] then begin // make logo rotate left and grow - //myScale:=(CTime-Timings[4])/(Timings[7]-Timings[4]); - glRotatef(20*sqr(CTime-Timings[3])/sqr((Timings[7]-Timings[3])/2),0,0,1); - glScalef(1+sqr(CTime-Timings[3])/(32*(Timings[7]-Timings[3])),1+sqr(CTime-Timings[3])/(32*(Timings[7]-Timings[3])),1); - end; - if CTime < Timings[2] then - glRotatef(30*myAngle,0.5*myScale+myScale,1+myScale,0); - //glScalef(0.5,0.5,0.5); - glScalef(4/3,-1,1); - glColor4f(1, 1, 1, 1); - - glBindTexture(GL_TEXTURE_2D, intro_layer01.TexNum); - glbegin(gl_quads); - glTexCoord2f(0,0);glVertex3f(-1, -1, -0.4 * myScale); - glTexCoord2f(0,1);glVertex3f(-1, 1, -0.4 * myScale); - glTexCoord2f(1,1); glVertex3f(1, 1, -0.4 * myScale); - glTexCoord2f(1,0);glVertex3f(1, -1, -0.4 * myScale); - glEnd; - glBindTexture(GL_TEXTURE_2D, intro_layer02.TexNum); - glbegin(gl_quads); - glTexCoord2f(0,0);glVertex3f(-1, -1, -0.3 * myScale); - glTexCoord2f(0,1);glVertex3f(-1, 1, -0.3 * myScale); - glTexCoord2f(1,1); glVertex3f(1, 1, -0.3 * myScale); - glTexCoord2f(1,0);glVertex3f(1, -1, -0.3 * myScale); - glEnd; - glBindTexture(GL_TEXTURE_2D, intro_layer03.TexNum); - glbegin(gl_quads); - glTexCoord2f(0,0);glVertex3f(-1, -1, -0.2 * myScale); - glTexCoord2f(0,1);glVertex3f(-1, 1, -0.2 * myScale); - glTexCoord2f(1,1); glVertex3f(1, 1, -0.2 * myScale); - glTexCoord2f(1,0);glVertex3f(1, -1, -0.2 * myScale); - glEnd; - glBindTexture(GL_TEXTURE_2D, intro_layer04.TexNum); - glbegin(gl_quads); - glTexCoord2f(0,0);glVertex3f(-1, -1, -0.1 * myScale); - glTexCoord2f(0,1);glVertex3f(-1, 1, -0.1 * myScale); - glTexCoord2f(1,1); glVertex3f(1, 1, -0.1 * myScale); - glTexCoord2f(1,0);glVertex3f(1, -1, -0.1 * myScale); - glEnd; - glBindTexture(GL_TEXTURE_2D, intro_layer05.TexNum); - glbegin(gl_quads); - glTexCoord2f(0,0);glVertex3f(-1, -1, 0 * myScale); - glTexCoord2f(0,1);glVertex3f(-1, 1, 0 * myScale); - glTexCoord2f(1,1); glVertex3f(1, 1, 0 * myScale); - glTexCoord2f(1,0);glVertex3f(1, -1, 0 * myScale); - glEnd; - glBindTexture(GL_TEXTURE_2D, intro_layer06.TexNum); - glbegin(gl_quads); - glTexCoord2f(0,0);glVertex3f(-1, -1, 0.1 * myScale); - glTexCoord2f(0,1);glVertex3f(-1, 1, 0.1 * myScale); - glTexCoord2f(1,1); glVertex3f(1, 1, 0.1 * myScale); - glTexCoord2f(1,0);glVertex3f(1, -1, 0.1 * myScale); - glEnd; - glBindTexture(GL_TEXTURE_2D, intro_layer07.TexNum); - glbegin(gl_quads); - glTexCoord2f(0,0);glVertex3f(-1, -1, 0.2 * myScale); - glTexCoord2f(0,1);glVertex3f(-1, 1, 0.2 * myScale); - glTexCoord2f(1,1); glVertex3f(1, 1, 0.2 * myScale); - glTexCoord2f(1,0);glVertex3f(1, -1, 0.2 * myScale); - glEnd; - glBindTexture(GL_TEXTURE_2D, intro_layer08.TexNum); - glbegin(gl_quads); - glTexCoord2f(0,0);glVertex3f(-1, -1, 0.3 * myScale); - glTexCoord2f(0,1);glVertex3f(-1, 1, 0.3 * myScale); - glTexCoord2f(1,1); glVertex3f(1, 1, 0.3 * myScale); - glTexCoord2f(1,0);glVertex3f(1, -1, 0.3 * myScale); - glEnd; - glBindTexture(GL_TEXTURE_2D, intro_layer09.TexNum); - glbegin(gl_quads); - glTexCoord2f(0,0);glVertex3f(-1, -1, 0.22 * myScale); - glTexCoord2f(0,1);glVertex3f(-1, 1, 0.22 * myScale); - glTexCoord2f(1,1); glVertex3f(1, 1, 0.22 * myScale); - glTexCoord2f(1,0);glVertex3f(1, -1, 0.22 * myScale); - glEnd; - gldisable(gl_texture_2d); - glDisable(GL_BLEND); + glEnable(GL_TEXTURE_2D); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_BLEND); - glPopMatrix; - End3D; + if CTime < Timings[1] then + begin + myScale := 0.5 + 0.5 * (Timings[1] - CTime) / (Timings[1]); // slowly move layers together + myAngle := cos((CTime) * pi / ((Timings[1]) * 2)); // and make logo face towards camera + end + else + begin // this is the part when the logo stands still + myScale := 0.5; + myAngle := 0; + end; + if CTime > Timings[2] then + begin + myScale := 0.5 + 0.5 * (CTime - Timings[2]) / (Timings[3] - Timings[2]); // get some space between layers + myAngle := 0; + end; +// if CTime > Timings[3] then myScale := 1; // keep the space between layers + glTranslatef(0, 0, -5 + 0.5 * myScale); + if CTime > Timings[3] then + myScale := 1; // keep the space between layers + if CTime > Timings[3] then + begin // make logo rotate left and grow +// myScale := (CTime - Timings[4]) / (Timings[7] - Timings[4]); + glRotatef(20 * sqr(CTime - Timings[3]) / sqr((Timings[7] - Timings[3]) / 2), 0, 0, 1); + glScalef(1 + sqr(CTime - Timings[3]) / (32 * (Timings[7] - Timings[3])), 1 + sqr(CTime - Timings[3]) / (32 * (Timings[7] - Timings[3])), 1); + end; + if CTime < Timings[2] then + glRotatef(30 * myAngle, 0.5 * myScale + myScale, 1 + myScale, 0); +// glScalef(0.5, 0.5, 0.5); + glScalef(4/3, -1, 1); + glColor4f(1, 1, 1, 1); - // do some sparkling effects - if (CTime < Timings[1]) and (CTime > Timings[21]) then - begin - for k:=1 to 3 do begin - l:=410+floor((CTime-Timings[21])/(Timings[1]-Timings[21])*(536-410))+RandomRange(-5,5); - j:=floor((Timings[1]-CTime)/22)+RandomRange(285,301); - GoldenRec.Spawn(l, j, 1, 16, 0, -1, Flare, 0); - end; - end; + glBindTexture(GL_TEXTURE_2D, intro_layer01.TexNum); + glbegin(gl_quads); + glTexCoord2f(0, 0); glVertex3f(-1, -1, -0.4 * myScale); + glTexCoord2f(0, 1); glVertex3f(-1, 1, -0.4 * myScale); + glTexCoord2f(1, 1); glVertex3f( 1, 1, -0.4 * myScale); + glTexCoord2f(1, 0); glVertex3f( 1, -1, -0.4 * myScale); + glEnd; + glBindTexture(GL_TEXTURE_2D, intro_layer02.TexNum); + glbegin(gl_quads); + glTexCoord2f(0, 0); glVertex3f(-1, -1, -0.3 * myScale); + glTexCoord2f(0, 1); glVertex3f(-1, 1, -0.3 * myScale); + glTexCoord2f(1, 1); glVertex3f( 1, 1, -0.3 * myScale); + glTexCoord2f(1, 0); glVertex3f( 1, -1, -0.3 * myScale); + glEnd; + glBindTexture(GL_TEXTURE_2D, intro_layer03.TexNum); + glbegin(gl_quads); + glTexCoord2f(0, 0); glVertex3f(-1, -1, -0.2 * myScale); + glTexCoord2f(0, 1); glVertex3f(-1, 1, -0.2 * myScale); + glTexCoord2f(1, 1); glVertex3f( 1, 1, -0.2 * myScale); + glTexCoord2f(1, 0); glVertex3f( 1, -1, -0.2 * myScale); + glEnd; + glBindTexture(GL_TEXTURE_2D, intro_layer04.TexNum); + glbegin(gl_quads); + glTexCoord2f(0, 0); glVertex3f(-1, -1, -0.1 * myScale); + glTexCoord2f(0, 1); glVertex3f(-1, 1, -0.1 * myScale); + glTexCoord2f(1, 1); glVertex3f( 1, 1, -0.1 * myScale); + glTexCoord2f(1, 0); glVertex3f( 1, -1, -0.1 * myScale); + glEnd; + glBindTexture(GL_TEXTURE_2D, intro_layer05.TexNum); + glbegin(gl_quads); + glTexCoord2f(0, 0); glVertex3f(-1, -1, 0 * myScale); + glTexCoord2f(0, 1); glVertex3f(-1, 1, 0 * myScale); + glTexCoord2f(1, 1); glVertex3f( 1, 1, 0 * myScale); + glTexCoord2f(1, 0); glVertex3f( 1, -1, 0 * myScale); + glEnd; + glBindTexture(GL_TEXTURE_2D, intro_layer06.TexNum); + glbegin(gl_quads); + glTexCoord2f(0, 0); glVertex3f(-1, -1, 0.1 * myScale); + glTexCoord2f(0, 1); glVertex3f(-1, 1, 0.1 * myScale); + glTexCoord2f(1, 1); glVertex3f( 1, 1, 0.1 * myScale); + glTexCoord2f(1, 0); glVertex3f( 1, -1, 0.1 * myScale); + glEnd; + glBindTexture(GL_TEXTURE_2D, intro_layer07.TexNum); + glbegin(gl_quads); + glTexCoord2f(0, 0); glVertex3f(-1, -1, 0.2 * myScale); + glTexCoord2f(0, 1); glVertex3f(-1, 1, 0.2 * myScale); + glTexCoord2f(1, 1); glVertex3f( 1, 1, 0.2 * myScale); + glTexCoord2f(1, 0); glVertex3f( 1, -1, 0.2 * myScale); + glEnd; + glBindTexture(GL_TEXTURE_2D, intro_layer08.TexNum); + glbegin(gl_quads); + glTexCoord2f(0, 0); glVertex3f(-1, -1, 0.3 * myScale); + glTexCoord2f(0, 1); glVertex3f(-1, 1, 0.3 * myScale); + glTexCoord2f(1, 1); glVertex3f( 1, 1, 0.3 * myScale); + glTexCoord2f(1, 0); glVertex3f( 1, -1, 0.3 * myScale); + glEnd; + glBindTexture(GL_TEXTURE_2D, intro_layer09.TexNum); + glbegin(gl_quads); + glTexCoord2f(0, 0); glVertex3f(-1, -1, 0.22 * myScale); + glTexCoord2f(0, 1); glVertex3f(-1, 1, 0.22 * myScale); + glTexCoord2f(1, 1); glVertex3f( 1, 1, 0.22 * myScale); + glTexCoord2f(1, 0); glVertex3f( 1, -1, 0.22 * myScale); + glEnd; + gldisable(gl_texture_2d); + glDisable(GL_BLEND); + + glPopMatrix; + End3D; + + // do some sparkling effects + if (CTime < Timings[1]) and (CTime > Timings[21]) then + begin + for k:= 1 to 3 do + begin + l := 410 + floor((CTime - Timings[21]) / (Timings[1] - Timings[21]) * (536 - 410)) + RandomRange(-5, 5); + j := floor((Timings[1] - CTime) / 22) + RandomRange(285, 301); + GoldenRec.Spawn(l, j, 1, 16, 0, -1, Flare, 0); + end; + end; - // fade to white at end - if Ctime > Timings[6] then - begin - glColor4f(1,1,1,sqr(Ctime-Timings[6])*(Ctime-Timings[6])/sqr(Timings[7]-Timings[6])); - glEnable(GL_BLEND); - glBegin(GL_QUADS); - glVertex2f(0,0); - glVertex2f(0,600); - glVertex2f(800,600); - glVertex2f(800,0); - glEnd; - glDisable(GL_BLEND); - end; + // fade to white at end + if Ctime > Timings[6] then + begin + glColor4f(1, 1, 1, sqr(CTime - Timings[6]) * (CTime - Timings[6]) / sqr(Timings[7] - Timings[6])); + glEnable(GL_BLEND); + glBegin(GL_QUADS); + glVertex2f( 0, 0); + glVertex2f( 0, 600); + glVertex2f(800, 600); + glVertex2f(800, 0); + glEnd; + glDisable(GL_BLEND); + end; - end; + end; if (CRDTS_Stage=MainPart) then // main credits screen background, scroller, logo and girl begin @@ -615,10 +569,10 @@ begin glColor4f(1, 1, 1, 1); glBindTexture(GL_TEXTURE_2D, credits_bg_tex.TexNum); glbegin(gl_quads); - glTexCoord2f(0,0);glVertex2f(0, 0); - glTexCoord2f(0,600/1024);glVertex2f(0, 600); - glTexCoord2f(800/1024,600/1024); glVertex2f(800, 600); - glTexCoord2f(800/1024,0);glVertex2f(800, 0); + glTexCoord2f( 0, 0); glVertex2f( 0, 0); + glTexCoord2f( 0, 600/1024); glVertex2f( 0, 600); + glTexCoord2f(800/1024, 600/1024); glVertex2f(800, 600); + glTexCoord2f(800/1024, 0); glVertex2f(800, 0); glEnd; glDisable(GL_TEXTURE_2D); glDisable(GL_BLEND); @@ -629,38 +583,50 @@ begin //######################################################################### // draw credits names - - Log.LogStatus('',' JB-4'); + Log.LogStatus('', ' JB-4'); // BlindGuard (rotate in from upper left, rotate out to lower right) - STime:=Timings[9]-10; - Delay:=Timings[10]-Timings[9]; + STime := Timings[9] - 10; + Delay := Timings[10] - Timings[9]; if CTime > STime then begin - k:=0; - ESC_Alpha:=20; + k := 0; + ESC_Alpha := 20; try - for j:=0 to 40 do + for j := 0 to 40 do begin - if ( j < length( Data ) ) AND - ( k < length( Data ) ) then + if (j < length(Data)) and + (k < length(Data)) then begin if Data[j] >= Data[k] then - k:=j; + k := j; end; end; except end; - if Data[k]>0.25 then ESC_Alpha:=5 else inc(ESC_Alpha); - if ESC_Alpha >20 then ESC_Alpha:=20; - if ((CTime-STime)<20) then ESC_Alpha:=20; - if CTime <=STime+10 then j:=CTime-STime else j:=10; - if (CTime >=STime+Delay-10) then if (CTime <=STime+Delay) then j:=(STime+Delay)-CTime else j:=0; - glColor4f(1, 1, 1, ESC_Alpha/20*j/10); - - if (CTime >= STime+10) and (CTime<=STime+12) then begin + if Data[k] > 0.25 then + ESC_Alpha := 5 + else + inc(ESC_Alpha); + if ESC_Alpha > 20 then + ESC_Alpha := 20; + if ((CTime - STime) < 20) then + ESC_Alpha := 20; + if CTime <= STime + 10 then + j := CTime - STime + else + j := 10; + if (CTime >= STime + Delay - 10) then + if (CTime <= STime + Delay) then + j := (STime + Delay) - CTime + else + j := 0; + glColor4f(1, 1, 1, ESC_Alpha / 20 * j / 10); + + if (CTime >= STime + 10) and (CTime <= STime + 12) then + begin GoldenRec.Spawn(RandomRange(65,390), RandomRange(200,460), 1, 16, 0, -1, PerfectLineTwinkle, 0); GoldenRec.Spawn(RandomRange(65,390), RandomRange(200,460), 1, 16, 0, -1, PerfectLineTwinkle, 1); GoldenRec.Spawn(RandomRange(65,390), RandomRange(200,460), 1, 16, 0, -1, PerfectLineTwinkle, 5); @@ -673,23 +639,26 @@ begin end; glPushMatrix; - gltranslatef(0,329,0); - if CTime <= STime+10 then begin glrotatef((CTime-STime)*9+270,0,0,1);end; - gltranslatef(223,0,0); - if CTime >=STime+Delay-10 then if CTime <=STime+Delay then begin - gltranslatef(223,0,0); - glrotatef((integer(CTime)-(integer(STime+Delay)-10))*-9,0,0,1); - gltranslatef(-223,0,0); - end; + gltranslatef(0, 329, 0); + if CTime <= STime + 10 then + glrotatef((CTime - STime) * 9 + 270, 0, 0, 1); + gltranslatef(223, 0, 0); + if CTime >= STime + Delay - 10 then + if CTime <= STime + Delay then + begin + gltranslatef(223, 0, 0); + glrotatef((integer(CTime) - (integer(STime + Delay) - 10)) * -9, 0, 0, 1); + gltranslatef(-223, 0, 0); + end; glBindTexture(GL_TEXTURE_2D, credits_blindguard.TexNum); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_BLEND); glEnable(GL_TEXTURE_2D); glbegin(gl_quads); - glTexCoord2f(0,0);glVertex2f(-163, -129); - glTexCoord2f(0,1);glVertex2f(-163, 129); - glTexCoord2f(1,1); glVertex2f(163, 129); - glTexCoord2f(1,0);glVertex2f(163, -129); + glTexCoord2f(0, 0); glVertex2f(-163, -129); + glTexCoord2f(0, 1); glVertex2f(-163, 129); + glTexCoord2f(1, 1); glVertex2f( 163, 129); + glTexCoord2f(1, 0); glVertex2f( 163, -129); glEnd; gldisable(gl_texture_2d); gldisable(GL_BLEND); @@ -697,35 +666,47 @@ begin end; // Blindy (zoom from 0 to full size and rotation, zoom zo doubble size and shift to upper right) - STime:=Timings[10]-10; - Delay:=Timings[11]-Timings[10]+5; + STime := Timings[10] - 10; + Delay := Timings[11] - Timings[10] + 5; if CTime > STime then begin - k:=0; - ESC_Alpha:=20; + k := 0; + ESC_Alpha := 20; try - for j:=0 to 40 do + for j := 0 to 40 do begin - if ( j < length( Data ) ) AND - ( k < length( Data ) ) then + if (j < length(Data)) and + (k < length(Data)) then begin if Data[j] >= Data[k] then - k:=j; + k := j; end; end; except end; - - if Data[k]>0.25 then ESC_Alpha:=5 else inc(ESC_Alpha); - if ESC_Alpha >20 then ESC_Alpha:=20; - if ((CTime-STime)<20) then ESC_Alpha:=20; - if CTime <=STime+10 then j:=CTime-STime else j:=10; - if (CTime >=STime+Delay-10) then if (CTime <=STime+Delay) then j:=(STime+Delay)-CTime else j:=0; - glColor4f(1, 1, 1, ESC_Alpha/20*j/10); - - if (CTime >= STime+20) and (CTime<=STime+22) then begin + if Data[k] > 0.25 then + ESC_Alpha := 5 + else + inc(ESC_Alpha); + if ESC_Alpha > 20 then + ESC_Alpha := 20; + if ((CTime - STime) < 20) then + ESC_Alpha := 20; + if CTime <= STime + 10 then + j := CTime - STime + else + j := 10; + if (CTime >= STime + Delay - 10) then + if (CTime <= STime + Delay) then + j := (STime + Delay) - CTime + else + j := 0; + glColor4f(1, 1, 1, ESC_Alpha / 20 * j / 10); + + if (CTime >= STime+20) and (CTime<=STime+22) then + begin GoldenRec.Spawn(RandomRange(65,390), RandomRange(200,460), 1, 16, 0, -1, PerfectLineTwinkle, 0); GoldenRec.Spawn(RandomRange(65,390), RandomRange(200,460), 1, 16, 0, -1, PerfectLineTwinkle, 1); GoldenRec.Spawn(RandomRange(65,390), RandomRange(200,460), 1, 16, 0, -1, PerfectLineTwinkle, 5); @@ -738,28 +719,31 @@ begin end; glPushMatrix; - gltranslatef(223,329,0); - if CTime <= STime+20 then begin - j:=CTime-Stime; - glscalef(j*j/400,j*j/400,j*j/400); - glrotatef(j*18.0,0,0,1); - end; - if CTime >=STime+Delay-10 then if CTime <=STime+Delay then begin - j:=CTime-(STime+Delay-10); - f:=j*10.0; - gltranslatef(f*3,-f,0); - glscalef(1+j/10,1+j/10,1+j/10); - glrotatef(j*9.0,0,0,1); + gltranslatef(223, 329, 0); + if CTime <= STime + 20 then + begin + j := CTime - Stime; + glscalef(j * j / 400, j * j / 400, j * j / 400); + glrotatef(j * 18.0, 0, 0, 1); end; + if CTime >= STime + Delay - 10 then + if CTime <= STime + Delay then + begin + j := CTime - (STime + Delay - 10); + f := j * 10.0; + gltranslatef(f * 3, -f, 0); + glscalef(1 + j / 10, 1 + j / 10, 1 + j / 10); + glrotatef(j * 9.0, 0, 0, 1); + end; glBindTexture(GL_TEXTURE_2D, credits_blindy.TexNum); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_BLEND); glEnable(GL_TEXTURE_2D); glbegin(gl_quads); - glTexCoord2f(0,0);glVertex2f(-163, -129); - glTexCoord2f(0,1);glVertex2f(-163, 129); - glTexCoord2f(1,1); glVertex2f(163, 129); - glTexCoord2f(1,0);glVertex2f(163, -129); + glTexCoord2f(0, 0); glVertex2f(-163, -129); + glTexCoord2f(0, 1); glVertex2f(-163, 129); + glTexCoord2f(1, 1); glVertex2f( 163, 129); + glTexCoord2f(1, 0); glVertex2f( 163, -129); glEnd; gldisable(gl_texture_2d); gldisable(GL_BLEND); @@ -767,35 +751,47 @@ begin end; // Canni (shift in from left, shift out to upper right) - STime:=Timings[11]-10; - Delay:=Timings[12]-Timings[11]+5; + STime := Timings[11] - 10; + Delay := Timings[12] - Timings[11] + 5; if CTime > STime then begin - k:=0; - ESC_Alpha:=20; + k := 0; + ESC_Alpha := 20; try - for j:=0 to 40 do + for j := 0 to 40 do begin - if ( j < length( Data ) ) AND - ( k < length( Data ) ) then + if (j < length(Data)) and + (k < length(Data)) then begin if Data[j] >= Data[k] then - k:=j; + k := j; end; end; except end; - - if Data[k]>0.25 then ESC_Alpha:=5 else inc(ESC_Alpha); - if ESC_Alpha >20 then ESC_Alpha:=20; - if ((CTime-STime)<20) then ESC_Alpha:=20; - if CTime <=STime+10 then j:=CTime-STime else j:=10; - if (CTime >=STime+Delay-10) then if (CTime <=STime+Delay) then j:=(STime+Delay)-CTime else j:=0; - glColor4f(1, 1, 1, ESC_Alpha/20*j/10); - - if (CTime >= STime+10) and (CTime<=STime+12) then begin + if Data[k] > 0.25 then + ESC_Alpha := 5 + else + inc(ESC_Alpha); + if ESC_Alpha > 20 then + ESC_Alpha := 20; + if ((CTime - STime) < 20) then + ESC_Alpha := 20; + if CTime <= STime + 10 then + j := CTime - STime + else + j := 10; + if (CTime >= STime + Delay - 10) then + if (CTime <= STime + Delay) then + j := (STime + Delay) - CTime + else + j := 0; + glColor4f(1, 1, 1, ESC_Alpha / 20 * j / 10); + + if (CTime >= STime + 10) and (CTime <= STime + 12) then + begin GoldenRec.Spawn(RandomRange(65,390), RandomRange(200,460), 1, 16, 0, -1, PerfectLineTwinkle, 0); GoldenRec.Spawn(RandomRange(65,390), RandomRange(200,460), 1, 16, 0, -1, PerfectLineTwinkle, 1); GoldenRec.Spawn(RandomRange(65,390), RandomRange(200,460), 1, 16, 0, -1, PerfectLineTwinkle, 5); @@ -808,23 +804,26 @@ begin end; glPushMatrix; - gltranslatef(223,329,0); - if CTime <= STime+10 then begin - gltranslatef(((CTime-STime)*21.0)-210,0,0); - end; - if CTime >=STime+Delay-10 then if CTime <=STime+Delay then begin - j:=(CTime-(STime+Delay-10))*21; - gltranslatef(j,-j/2,0); + gltranslatef(223, 329, 0); + if CTime <= STime + 10 then + begin + gltranslatef(((CTime - STime) * 21.0) - 210, 0, 0); end; + if CTime >= STime + Delay - 10 then + if CTime <= STime + Delay then + begin + j := (CTime - (STime + Delay - 10)) * 21; + gltranslatef(j, -j / 2, 0); + end; glBindTexture(GL_TEXTURE_2D, credits_canni.TexNum); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_BLEND); glEnable(GL_TEXTURE_2D); glbegin(gl_quads); - glTexCoord2f(0,0);glVertex2f(-163, -129); - glTexCoord2f(0,1);glVertex2f(-163, 129); - glTexCoord2f(1,1); glVertex2f(163, 129); - glTexCoord2f(1,0);glVertex2f(163, -129); + glTexCoord2f(0, 0); glVertex2f(-163, -129); + glTexCoord2f(0, 1); glVertex2f(-163, 129); + glTexCoord2f(1, 1); glVertex2f( 163, 129); + glTexCoord2f(1, 0); glVertex2f( 163, -129); glEnd; gldisable(gl_texture_2d); gldisable(GL_BLEND); @@ -832,35 +831,47 @@ begin end; // Commandio (flip in from down, flip out to upper right) - STime:=Timings[12]-10; - Delay:=Timings[13]-Timings[12]; + STime := Timings[12] - 10; + Delay := Timings[13] - Timings[12]; if CTime > STime then begin - k:=0; - ESC_Alpha:=20; + k := 0; + ESC_Alpha := 20; try - for j:=0 to 40 do + for j := 0 to 40 do begin - if ( j < length( Data ) ) AND - ( k < length( Data ) ) then + if (j < length(Data)) and + (k < length(Data)) then begin if Data[j] >= Data[k] then - k:=j; + k := j; end; end; except end; - - if Data[k]>0.25 then ESC_Alpha:=5 else inc(ESC_Alpha); - if ESC_Alpha >20 then ESC_Alpha:=20; - if ((CTime-STime)<20) then ESC_Alpha:=20; - if CTime <=STime+10 then j:=CTime-STime else j:=10; - if (CTime >=STime+Delay-10) then if (CTime <=STime+Delay) then j:=(STime+Delay)-CTime else j:=0; - glColor4f(1, 1, 1, ESC_Alpha/20*j/10); - - if (CTime >= STime+10) and (CTime<=STime+12) then begin + if Data[k] > 0.25 then + ESC_Alpha := 5 + else + inc(ESC_Alpha); + if ESC_Alpha > 20 then + ESC_Alpha := 20; + if ((CTime - STime) < 20) then + ESC_Alpha := 20; + if CTime <= STime + 10 then + j := CTime - STime + else + j := 10; + if (CTime >= STime + Delay - 10) then + if (CTime <= STime + Delay) then + j := (STime + Delay) - CTime + else + j := 0; + glColor4f(1, 1, 1, ESC_Alpha / 20 * j / 10); + + if (CTime >= STime + 10) and (CTime <= STime + 12) then + begin GoldenRec.Spawn(RandomRange(65,390), RandomRange(200,460), 1, 16, 0, -1, PerfectLineTwinkle, 0); GoldenRec.Spawn(RandomRange(65,390), RandomRange(200,460), 1, 16, 0, -1, PerfectLineTwinkle, 1); GoldenRec.Spawn(RandomRange(65,390), RandomRange(200,460), 1, 16, 0, -1, PerfectLineTwinkle, 5); @@ -873,25 +884,27 @@ begin end; glPushMatrix; - gltranslatef(223,329,0); - if CTime <= STime+10 then - f:=258.0-25.8*(CTime-STime) + gltranslatef(223, 329, 0); + if CTime <= STime + 10 then + f := 258.0 - 25.8 * (CTime - STime) else - f:=0; - g:=0; - if CTime >=STime+Delay-10 then if CTime <=STime+Delay then begin - j:=CTime-(STime+Delay-10); - g:=32.6*j; - end; + f := 0; + g := 0; + if CTime >= STime + Delay - 10 then + if CTime <= STime + Delay then + begin + j := CTime - (STime + Delay - 10); + g := 32.6 * j; + end; glBindTexture(GL_TEXTURE_2D, credits_commandio.TexNum); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_BLEND); glEnable(GL_TEXTURE_2D); glbegin(gl_quads); - glTexCoord2f(0,0);glVertex2f(-163+g-f*1.5, -129+f*1.5-g/2); - glTexCoord2f(0,1);glVertex2f(-163+g*1.5, 129-(g*1.5*258/326)); - glTexCoord2f(1,1); glVertex2f(163+g, 129+g/4); - glTexCoord2f(1,0);glVertex2f(163+f*1.5+g/4, -129+f*1.5-g/4); + glTexCoord2f(0, 0); glVertex2f(-163 + g - f * 1.5, -129 + f * 1.5 - g/2); + glTexCoord2f(0, 1); glVertex2f(-163 + g * 1.5, 129 - (g * 1.5 * 258 / 326)); + glTexCoord2f(1, 1); glVertex2f( 163 + g, 129 + g / 4); + glTexCoord2f(1, 0); glVertex2f( 163 + f * 1.5 + g / 4, -129 + f * 1.5 - g / 4); glEnd; gldisable(gl_texture_2d); gldisable(GL_BLEND); @@ -899,46 +912,57 @@ begin end; // lazy joker (just scrolls from left to right, no twinkling stars, no on-beat flashing) - STime:=Timings[13]-35; - Delay:=Timings[14]-Timings[13]+5; + STime := Timings[13] - 35; + Delay := Timings[14] - Timings[13] + 5; if CTime > STime then begin - k:=0; + k := 0; try - for j:=0 to 40 do + for j := 0 to 40 do begin - if ( j < length( Data ) ) AND - ( k < length( Data ) ) then + if (j < length(Data)) and + (k < length(Data)) then begin if Data[j] >= Data[k] then - k:=j; + k := j; end; end; except end; - - if Data[k]>0.25 then ESC_Alpha:=5 else inc(ESC_Alpha); - if ESC_Alpha >20 then ESC_Alpha:=20; - if ((CTime-STime)>10) and ((CTime-STime)<20) then ESC_Alpha:=20; - ESC_Alpha:=10; - f:=CTime-STime; - if CTime <=STime+40 then j:=CTime-STime else j:=40; - if (CTime >=STime+Delay-40) then if (CTime <=STime+Delay) then j:=(STime+Delay)-CTime else j:=0; - glColor4f(1, 1, 1, ESC_Alpha/20*j*j/1600); + if Data[k] > 0.25 then + ESC_Alpha := 5 + else + inc(ESC_Alpha); + if ESC_Alpha > 20 then + ESC_Alpha := 20; + if ((CTime - STime) > 10) and ((CTime - STime) < 20) then + ESC_Alpha := 20; + ESC_Alpha := 10; + f := CTime - STime; + if CTime <= STime + 40 then + j := CTime - STime + else + j := 40; + if (CTime >= STime + Delay - 40) then + if (CTime <= STime + Delay) then + j := (STime + Delay) - CTime + else + j := 0; + glColor4f(1, 1, 1, ESC_Alpha / 20 * j * j / 1600); glPushMatrix; - gltranslatef(180+(f-70),329,0); + gltranslatef(180 + (f - 70), 329, 0); glBindTexture(GL_TEXTURE_2D, credits_lazyjoker.TexNum); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_BLEND); glEnable(GL_TEXTURE_2D); glbegin(gl_quads); - glTexCoord2f(0,0);glVertex2f(-163, -129); - glTexCoord2f(0,1);glVertex2f(-163, 129); - glTexCoord2f(1,1); glVertex2f(163, 129); - glTexCoord2f(1,0);glVertex2f(163, -129); + glTexCoord2f(0, 0); glVertex2f(-163, -129); + glTexCoord2f(0, 1); glVertex2f(-163, 129); + glTexCoord2f(1, 1); glVertex2f( 163, 129); + glTexCoord2f(1, 0); glVertex2f( 163, -129); glEnd; gldisable(gl_texture_2d); gldisable(GL_BLEND); @@ -946,36 +970,47 @@ begin end; // Mog (flip in from right, flip out to lower right) - STime:=Timings[14]-10; - Delay:=Timings[15]-Timings[14]+5; + STime := Timings[14] - 10; + Delay := Timings[15] - Timings[14] + 5; if CTime > STime then begin - k:=0; - ESC_Alpha:=20; - + k := 0; + ESC_Alpha := 20; try - for j:=0 to 40 do + for j := 0 to 40 do begin - if ( j < length( Data ) ) AND - ( k < length( Data ) ) then + if (j < length(Data)) and + (k < length(Data)) then begin if Data[j] >= Data[k] then - k:=j; + k := j; end; end; except end; - - if Data[k]>0.25 then ESC_Alpha:=5 else inc(ESC_Alpha); - if ESC_Alpha >20 then ESC_Alpha:=20; - if ((CTime-STime)<20) then ESC_Alpha:=20; - if CTime <=STime+10 then j:=CTime-STime else j:=10; - if (CTime >=STime+Delay-10) then if (CTime <=STime+Delay) then j:=(STime+Delay)-CTime else j:=0; - glColor4f(1, 1, 1, ESC_Alpha/20*j/10); - - if (CTime >= STime+10) and (CTime<=STime+12) then begin + if Data[k] > 0.25 then + ESC_Alpha := 5 + else + inc(ESC_Alpha); + if ESC_Alpha > 20 then + ESC_Alpha := 20; + if ((CTime - STime) < 20) then + ESC_Alpha := 20; + if CTime <= STime + 10 then + j := CTime - STime + else + j := 10; + if (CTime >= STime + Delay - 10) then + if (CTime <= STime + Delay) then + j := (STime + Delay) - CTime + else + j := 0; + glColor4f(1, 1, 1, ESC_Alpha / 20 * j / 10); + + if (CTime >= STime + 10) and (CTime <= STime + 12) then + begin GoldenRec.Spawn(RandomRange(65,390), RandomRange(200,460), 1, 16, 0, -1, PerfectLineTwinkle, 0); GoldenRec.Spawn(RandomRange(65,390), RandomRange(200,460), 1, 16, 0, -1, PerfectLineTwinkle, 1); GoldenRec.Spawn(RandomRange(65,390), RandomRange(200,460), 1, 16, 0, -1, PerfectLineTwinkle, 5); @@ -988,26 +1023,28 @@ begin end; glPushMatrix; - gltranslatef(223,329,0); - if CTime <= STime+10 then - f:=326.0-32.6*(CTime-STime) + gltranslatef(223, 329, 0); + if CTime <= STime + 10 then + f := 326.0 - 32.6 * (CTime - STime) else - f:=0; - - g:=0; - if CTime >=STime+Delay-10 then if CTime <=STime+Delay then begin - j:=CTime-(STime+Delay-10); - g:=32.6*j; - end; + f := 0; + + g := 0; + if CTime >= STime + Delay - 10 then + if CTime <= STime + Delay then + begin + j := CTime - (STime + Delay - 10); + g := 32.6 * j; + end; glBindTexture(GL_TEXTURE_2D, credits_mog.TexNum); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_BLEND); glEnable(GL_TEXTURE_2D); glbegin(gl_quads); - glTexCoord2f(0,0);glVertex2f(-163+g*1.5, -129+g*1.5); - glTexCoord2f(0,1);glVertex2f(-163+g*1.2, 129+g); - glTexCoord2f(1,1); glVertex2f(163-f+g/2, 129+f*1.5+g/4); - glTexCoord2f(1,0);glVertex2f(163-f+g*1.5, -129-f*1.5); + glTexCoord2f(0, 0); glVertex2f(-163 + g * 1.5, -129 + g * 1.5); + glTexCoord2f(0, 1); glVertex2f(-163 + g * 1.2, 129 + g); + glTexCoord2f(1, 1); glVertex2f( 163 - f + g / 2, 129 + f * 1.5 + g / 4); + glTexCoord2f(1, 0); glVertex2f( 163 - f + g * 1.5, -129 - f * 1.5); glEnd; gldisable(gl_texture_2d); gldisable(GL_BLEND); @@ -1015,35 +1052,47 @@ begin end; // Mota (rotate in from upper right, shift out to lower left while shrinking and rotateing) - STime:=Timings[15]-10; - Delay:=Timings[16]-Timings[15]+5; + STime := Timings[15] - 10; + Delay := Timings[16] - Timings[15] + 5; if CTime > STime then begin - k:=0; - ESC_Alpha:=20; + k := 0; + ESC_Alpha := 20; try - for j:=0 to 40 do + for j := 0 to 40 do begin - if ( j < length( Data ) ) AND - ( k < length( Data ) ) then + if (j < length(Data)) and + (k < length(Data)) then begin if Data[j] >= Data[k] then - k:=j; + k := j; end; end; except end; - - if Data[k]>0.25 then ESC_Alpha:=5 else inc(ESC_Alpha); - if ESC_Alpha >20 then ESC_Alpha:=20; - if ((CTime-STime)<20) then ESC_Alpha:=20; - if CTime <=STime+10 then j:=CTime-STime else j:=10; - if (CTime >=STime+Delay-10) then if (CTime <=STime+Delay) then j:=(STime+Delay)-CTime else j:=0; - glColor4f(1, 1, 1, ESC_Alpha/20*j/10); - - if (CTime >= STime+10) and (CTime<=STime+12) then begin + if Data[k] > 0.25 then + ESC_Alpha := 5 + else + inc(ESC_Alpha); + if ESC_Alpha > 20 then + ESC_Alpha := 20; + if ((CTime - STime) < 20) then + ESC_Alpha := 20; + if CTime <= STime + 10 then + j := CTime - STime + else + j := 10; + if (CTime >= STime + Delay - 10) then + if (CTime <= STime + Delay) then + j := (STime + Delay) - CTime + else + j := 0; + glColor4f(1, 1, 1, ESC_Alpha / 20 * j / 10); + + if (CTime >= STime + 10) and (CTime <= STime + 12) then + begin GoldenRec.Spawn(RandomRange(65,390), RandomRange(200,460), 1, 16, 0, -1, PerfectLineTwinkle, 0); GoldenRec.Spawn(RandomRange(65,390), RandomRange(200,460), 1, 16, 0, -1, PerfectLineTwinkle, 1); GoldenRec.Spawn(RandomRange(65,390), RandomRange(200,460), 1, 16, 0, -1, PerfectLineTwinkle, 5); @@ -1056,28 +1105,31 @@ begin end; glPushMatrix; - gltranslatef(223,329,0); - if CTime <= STime+10 then begin - gltranslatef(223,0,0); - glrotatef((10-(CTime-STime))*9,0,0,1); - gltranslatef(-223,0,0); - end; - if CTime >=STime+Delay-10 then if CTime <=STime+Delay then begin - j:=CTime-(STime+Delay-10); - f:=j*10.0; - gltranslatef(-f*2,-f,0); - glscalef(1-j/10,1-j/10,1-j/10); - glrotatef(-j*9.0,0,0,1); + gltranslatef(223, 329, 0); + if CTime <= STime + 10 then + begin + gltranslatef(223, 0, 0); + glrotatef((10 - (CTime - STime)) * 9, 0, 0, 1); + gltranslatef(-223, 0, 0); end; + if CTime >= STime + Delay - 10 then + if CTime <= STime + Delay then + begin + j := CTime - (STime + Delay - 10); + f := j * 10.0; + gltranslatef(-f * 2, -f, 0); + glscalef(1 - j / 10, 1 - j / 10, 1 - j / 10); + glrotatef(-j * 9.0, 0, 0, 1); + end; glBindTexture(GL_TEXTURE_2D, credits_mota.TexNum); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_BLEND); glEnable(GL_TEXTURE_2D); glbegin(gl_quads); - glTexCoord2f(0,0);glVertex2f(-163, -129); - glTexCoord2f(0,1);glVertex2f(-163, 129); - glTexCoord2f(1,1); glVertex2f(163, 129); - glTexCoord2f(1,0);glVertex2f(163, -129); + glTexCoord2f(0, 0); glVertex2f(-163, -129); + glTexCoord2f(0, 1); glVertex2f(-163, 129); + glTexCoord2f(1, 1); glVertex2f( 163, 129); + glTexCoord2f(1, 0); glVertex2f( 163, -129); glEnd; gldisable(gl_texture_2d); gldisable(GL_BLEND); @@ -1085,35 +1137,47 @@ begin end; // Skillmaster (shift in from lower right, rotate out to upper right) - STime:=Timings[16]-10; - Delay:=Timings[17]-Timings[16]+5; + STime := Timings[16] - 10; + Delay := Timings[17] - Timings[16] + 5; if CTime > STime then begin - k:=0; - ESC_Alpha:=20; + k := 0; + ESC_Alpha := 20; try - for j:=0 to 40 do + for j := 0 to 40 do begin - if ( j < length( Data ) ) AND - ( k < length( Data ) ) then + if (j < length(Data)) and + (k < length(Data)) then begin if Data[j] >= Data[k] then - k:=j; + k := j; end; end; except end; - - if Data[k]>0.25 then ESC_Alpha:=5 else inc(ESC_Alpha); - if ESC_Alpha >20 then ESC_Alpha:=20; - if ((CTime-STime)<20) then ESC_Alpha:=20; - if CTime <=STime+10 then j:=CTime-STime else j:=10; - if (CTime >=STime+Delay-10) then if (CTime <=STime+Delay) then j:=(STime+Delay)-CTime else j:=0; - glColor4f(1, 1, 1, ESC_Alpha/20*j/10); - - if (CTime >= STime+10) and (CTime<=STime+12) then begin + if Data[k] > 0.25 then + ESC_Alpha := 5 + else + inc(ESC_Alpha); + if ESC_Alpha > 20 then + ESC_Alpha := 20; + if ((CTime - STime) < 20) then + ESC_Alpha := 20; + if CTime <= STime + 10 then + j := CTime - STime + else + j := 10; + if (CTime >= STime + Delay - 10) then + if (CTime <= STime + Delay) then + j := (STime + Delay) - CTime + else + j := 0; + glColor4f(1, 1, 1, ESC_Alpha / 20 * j / 10); + + if (CTime >= STime + 10) and (CTime <= STime + 12) then + begin GoldenRec.Spawn(RandomRange(65,390), RandomRange(200,460), 1, 16, 0, -1, PerfectLineTwinkle, 0); GoldenRec.Spawn(RandomRange(65,390), RandomRange(200,460), 1, 16, 0, -1, PerfectLineTwinkle, 1); GoldenRec.Spawn(RandomRange(65,390), RandomRange(200,460), 1, 16, 0, -1, PerfectLineTwinkle, 5); @@ -1126,28 +1190,31 @@ begin end; glPushMatrix; - gltranslatef(223,329,0); - if CTime <= STime+10 then begin - j:=STime+10-CTime; - f:=j*10.0; - gltranslatef(+f*2,+f/2,0); - end; - if CTime >=STime+Delay-10 then if CTime <=STime+Delay then begin - j:=CTime-(STime+Delay-10); - gltranslatef(0,-223,0); - glrotatef(integer(j)*-9,0,0,1); - gltranslatef(0,223,0); - glrotatef(j*9,0,0,1); + gltranslatef(223, 329, 0); + if CTime <= STime + 10 then + begin + j := STime + 10 - CTime; + f := j * 10.0; + gltranslatef(+f * 2, +f / 2, 0); end; + if CTime >= STime + Delay - 10 then + if CTime <= STime + Delay then + begin + j := CTime - (STime + Delay - 10); + gltranslatef(0, -223, 0); + glrotatef(integer(j) * -9, 0, 0, 1); + gltranslatef(0, 223, 0); + glrotatef(j * 9, 0, 0, 1); + end; glBindTexture(GL_TEXTURE_2D, credits_skillmaster.TexNum); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_BLEND); glEnable(GL_TEXTURE_2D); glbegin(gl_quads); - glTexCoord2f(0,0);glVertex2f(-163, -129); - glTexCoord2f(0,1);glVertex2f(-163, 129); - glTexCoord2f(1,1); glVertex2f(163, 129); - glTexCoord2f(1,0);glVertex2f(163, -129); + glTexCoord2f(0, 0); glVertex2f(-163, -129); + glTexCoord2f(0, 1); glVertex2f(-163, 129); + glTexCoord2f(1, 1); glVertex2f( 163, 129); + glTexCoord2f(1, 0); glVertex2f( 163, -129); glEnd; gldisable(gl_texture_2d); gldisable(GL_BLEND); @@ -1155,35 +1222,47 @@ begin end; // WhiteShark (flip in from lower left, flip out to upper right) - STime:=Timings[17]-10; - Delay:=Timings[18]-Timings[17]; + STime := Timings[17] - 10; + Delay := Timings[18] - Timings[17]; if CTime > STime then begin - k:=0; - ESC_Alpha:=20; + k := 0; + ESC_Alpha := 20; try - for j:=0 to 40 do + for j := 0 to 40 do begin - if ( j < length( Data ) ) AND - ( k < length( Data ) ) then + if (j < length(Data)) and + (k < length(Data)) then begin if Data[j] >= Data[k] then - k:=j; + k := j; end; end; except end; - - if Data[k]>0.25 then ESC_Alpha:=5 else inc(ESC_Alpha); - if ESC_Alpha >20 then ESC_Alpha:=20; - if ((CTime-STime)<20) then ESC_Alpha:=20; - if CTime <=STime+10 then j:=CTime-STime else j:=10; - if (CTime >=STime+Delay-10) then if (CTime <=STime+Delay) then j:=(STime+Delay)-CTime else j:=0; - glColor4f(1, 1, 1, ESC_Alpha/20*j/10); - - if (CTime >= STime+10) and (CTime<=STime+12) then begin + if Data[k] > 0.25 then + ESC_Alpha := 5 + else + inc(ESC_Alpha); + if ESC_Alpha > 20 then + ESC_Alpha := 20; + if ((CTime - STime) < 20) then + ESC_Alpha := 20; + if CTime <= STime + 10 then + j := CTime - STime + else + j := 10; + if (CTime >= STime + Delay - 10) then + if (CTime <= STime + Delay) then + j := (STime + Delay) - CTime + else + j := 0; + glColor4f(1, 1, 1, ESC_Alpha / 20 * j / 10); + + if (CTime >= STime + 10) and (CTime <= STime + 12) then + begin GoldenRec.Spawn(RandomRange(65,390), RandomRange(200,460), 1, 16, 0, -1, PerfectLineTwinkle, 0); GoldenRec.Spawn(RandomRange(65,390), RandomRange(200,460), 1, 16, 0, -1, PerfectLineTwinkle, 1); GoldenRec.Spawn(RandomRange(65,390), RandomRange(200,460), 1, 16, 0, -1, PerfectLineTwinkle, 5); @@ -1196,20 +1275,20 @@ begin end; glPushMatrix; - gltranslatef(223,329,0); - if CTime <= STime+10 then - f:=326.0-32.6*(CTime-STime) + gltranslatef(223, 329, 0); + if CTime <= STime + 10 then + f := 326.0 - 32.6 * (CTime - STime) else - f:=0; + f := 0; - if (CTime >= STime+Delay-10) and (CTime <= STime+Delay) then + if (CTime >= STime + Delay - 10) and (CTime <= STime + Delay) then begin - j:=CTime-(STime+Delay-10); - g:=32.6*j; + j := CTime - (STime + Delay - 10); + g := 32.6 * j; end else begin - g:=0; + g := 0; end; glBindTexture(GL_TEXTURE_2D, credits_whiteshark.TexNum); @@ -1217,51 +1296,50 @@ begin glEnable(GL_BLEND); glEnable(GL_TEXTURE_2D); glbegin(gl_quads); - glTexCoord2f(0,0);glVertex2f(-163-f+g, -129+f/4-g/2); - glTexCoord2f(0,1);glVertex2f(-163-f/4+g, 129+g/2+f/4); - glTexCoord2f(1,1); glVertex2f(163-f*1.2+g/4, 129+f/2-g/4); - glTexCoord2f(1,0);glVertex2f(163-f*1.5+g/4, -129+f*1.5+g/4); + glTexCoord2f(0, 0); glVertex2f(-163 - f + g, -129 + f / 4 - g / 2); + glTexCoord2f(0, 1); glVertex2f(-163 - f / 4 + g, 129 + g / 2 + f / 4); + glTexCoord2f(1, 1); glVertex2f( 163 - f * 1.2 + g / 4, 129 + f / 2 - g / 4); + glTexCoord2f(1, 0); glVertex2f( 163 - f * 1.5 + g / 4, -129 + f * 1.5 + g / 4); glEnd; gldisable(gl_texture_2d); gldisable(GL_BLEND); glPopMatrix; end; - - Log.LogStatus('',' JB-103'); + Log.LogStatus('', ' JB-103'); // #################################################################### // do some twinkle stuff (kinda on beat) - if (CTime > Timings[8] ) and - (CTime < Timings[19] ) then + if (CTime > Timings[8] ) and + (CTime < Timings[19]) then begin k := 0; try - for j:=0 to 40 do + for j := 0 to 40 do begin - if ( j < length( Data ) ) AND - ( k < length( Data ) ) then + if (j < length(Data)) and + (k < length(Data)) then begin if Data[j] >= Data[k] then - k:=j; + k := j; end; end; except end; - if Data[k]>0.2 then + if Data[k] > 0.2 then begin - l := RandomRange(6,16); - j := RandomRange(0,27); + l := RandomRange(6, 16); + j := RandomRange(0, 27); GoldenRec.Spawn(myLogoCoords[j,0], myLogoCoords[j,1], 16-l, l, 0, -1, PerfectNote, 0); end; end; //################################################# - // draw the rest of the main screen (girl and logo + // draw the rest of the main screen (girl and logo) glEnable(GL_TEXTURE_2D); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); @@ -1269,21 +1347,21 @@ begin glColor4f(1, 1, 1, 1); glBindTexture(GL_TEXTURE_2D, credits_bg_ovl.TexNum); glbegin(gl_quads); - glTexCoord2f(0,0);glVertex2f(800-393, 0); - glTexCoord2f(0,600/1024);glVertex2f(800-393, 600); - glTexCoord2f(393/512,600/1024); glVertex2f(800, 600); - glTexCoord2f(393/512,0);glVertex2f(800, 0); + glTexCoord2f( 0, 0); glVertex2f(800-393, 0); + glTexCoord2f( 0, 600/1024); glVertex2f(800-393, 600); + glTexCoord2f(393/512, 600/1024); glVertex2f(800, 600); + glTexCoord2f(393/512, 0); glVertex2f(800, 0); glEnd; - { +{ glBindTexture(GL_TEXTURE_2D, credits_bg_logo.TexNum); glbegin(gl_quads); - glTexCoord2f(0,0);glVertex2f(0, 0); - glTexCoord2f(0,112/128);glVertex2f(0, 112); - glTexCoord2f(497/512,112/128); glVertex2f(497, 112); - glTexCoord2f(497/512,0);glVertex2f(497, 0); + glTexCoord2f( 0, 0); glVertex2f( 0, 0); + glTexCoord2f( 0, 112/128); glVertex2f( 0, 112); + glTexCoord2f(497/512, 112/128); glVertex2f(497, 112); + glTexCoord2f(497/512, 0); glVertex2f(497, 0); glEnd; - } +} gldisable(gl_texture_2d); glDisable(GL_BLEND); @@ -1291,33 +1369,35 @@ begin // fade out at end of main part if Ctime > Timings[19] then begin - glColor4f(0,0,0,(Ctime-Timings[19])/(Timings[20]-Timings[19])); + glColor4f(0, 0, 0, (CTime - Timings[19]) / (Timings[20] - Timings[19])); glEnable(GL_BLEND); glBegin(GL_QUADS); - glVertex2f(0,0); - glVertex2f(0,600); - glVertex2f(800,600); - glVertex2f(800,0); + glVertex2f( 0, 0); + glVertex2f( 0, 600); + glVertex2f(800, 600); + glVertex2f(800, 0); glEnd; glDisable(GL_BLEND); end; end else - if (CRDTS_Stage=Outro) then + if (CRDTS_Stage = Outro) then begin - if CTime=Timings[20] then begin - CTime_hold:=0; + if CTime = Timings[20] then + begin + CTime_hold := 0; AudioPlayback.Stop; - AudioPlayback.Open(soundpath + 'credits-outro-tune.mp3'); + AudioPlayback.Open(SoundPath.Append('credits-outro-tune.mp3')); AudioPlayback.SetVolume(0.2); - AudioPlayback.SetLoop(True); + AudioPlayback.SetLoop(true); AudioPlayback.Play; end; - if CTime_hold > 231 then begin + if CTime_hold > 231 then + begin AudioPlayback.Play; - Ctime_hold:=0; + Ctime_hold := 0; end; - glClearColor(0,0,0,0); + glClearColor(0, 0, 0, 0); glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT); // do something useful @@ -1329,34 +1409,36 @@ begin glColor4f(1, 1, 1, 1); glBindTexture(GL_TEXTURE_2D, outro_bg.TexNum); glbegin(gl_quads); - glTexCoord2f(0,0);glVertex2f(0, 0); - glTexCoord2f(0,600/1024);glVertex2f(0, 600); - glTexCoord2f(800/1024,600/1024); glVertex2f(800, 600); - glTexCoord2f(800/1024,0);glVertex2f(800, 0); + glTexCoord2f( 0, 0); glVertex2f( 0, 0); + glTexCoord2f( 0, 600/1024); glVertex2f( 0, 600); + glTexCoord2f(800/1024, 600/1024); glVertex2f(800, 600); + glTexCoord2f(800/1024, 0); glVertex2f(800, 0); glEnd; - //outro overlays - glColor4f(1, 1, 1, (1+sin(CTime/15))/3+1/3); + // outro overlays + glColor4f(1, 1, 1, (1 + sin(CTime / 15)) / 3 + 1/3); glBindTexture(GL_TEXTURE_2D, outro_esc.TexNum); glbegin(gl_quads); - glTexCoord2f(0,0);glVertex2f(0, 0); - glTexCoord2f(0,223/256);glVertex2f(0, 223); - glTexCoord2f(487/512,223/256); glVertex2f(487, 223); - glTexCoord2f(487/512,0);glVertex2f(487, 0); + glTexCoord2f( 0, 0); glVertex2f( 0, 0); + glTexCoord2f( 0, 223/256); glVertex2f( 0, 223); + glTexCoord2f(487/512, 223/256); glVertex2f(487, 223); + glTexCoord2f(487/512, 0); glVertex2f(487, 0); glEnd; - ESC_Alpha:=20; - if (RandomRange(0,20) > 18) and (ESC_Alpha=20) then - ESC_Alpha:=0 - else inc(ESC_Alpha); - if ESC_Alpha > 20 then ESC_Alpha:=20; - glColor4f(1, 1, 1, ESC_Alpha/20); + ESC_Alpha := 20; + if (RandomRange(0,20) > 18) and (ESC_Alpha = 20) then + ESC_Alpha := 0 + else + inc(ESC_Alpha); + if ESC_Alpha > 20 then + ESC_Alpha := 20; + glColor4f(1, 1, 1, ESC_Alpha / 20); glBindTexture(GL_TEXTURE_2D, outro_exd.TexNum); glbegin(gl_quads); - glTexCoord2f(0,0);glVertex2f(800-310, 600-247); - glTexCoord2f(0,247/256);glVertex2f(800-310, 600); - glTexCoord2f(310/512,247/256); glVertex2f(800, 600); - glTexCoord2f(310/512,0);glVertex2f(800, 600-247); + glTexCoord2f( 0, 0); glVertex2f(800-310, 600-247); + glTexCoord2f( 0, 247/256); glVertex2f(800-310, 600 ); + glTexCoord2f(310/512, 247/256); glVertex2f(800, 600 ); + glTexCoord2f(310/512, 0); glVertex2f(800, 600-247); glEnd; glDisable(GL_TEXTURE_2D); glDisable(GL_BLEND); @@ -1365,17 +1447,17 @@ begin // ... end; - { +{ // draw credits runtime counter - SetFontStyle (2); - SetFontItalic(False); + SetFontStyle (2); + SetFontItalic(false); SetFontSize(27); SetFontPos (5, 5); glColor4f(1, 1, 1, 1); - //RuntimeStr:='CTime: '+inttostr(floor(CTime/30.320663991914489602156136106092))+'.'+inttostr(floor(CTime/3.0320663991914489602156136106092)-floor(CTime/30.320663991914489602156136106092)*10); - RuntimeStr:='CTime: '+inttostr(CTime); +// RuntimeStr := 'CTime: ' + inttostr(floor(CTime / 30.320663991914489602156136106092)) + '.' + inttostr(floor(CTime / 3.0320663991914489602156136106092) - floor(CTime / 30.320663991914489602156136106092) * 10); + RuntimeStr := 'CTime: ' + inttostr(CTime); glPrint (RuntimeStr[1]); - } +} // make the stars shine GoldenRec.Draw; diff --git a/Lua/src/screens/UScreenEdit.pas b/Lua/src/screens/UScreenEdit.pas index 5112e17a..2111adef 100644 --- a/Lua/src/screens/UScreenEdit.pas +++ b/Lua/src/screens/UScreenEdit.pas @@ -45,8 +45,7 @@ type TextDescriptionLong: integer; constructor Create; override; - function ParseInput(PressedKey: cardinal; CharCode: WideChar; - PressedDown: boolean): boolean; override; + function ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; override; procedure InteractNext; override; procedure InteractPrev; override; procedure InteractInc; override; @@ -60,10 +59,10 @@ uses UGraphic, UMusic, USkins, + UUnicodeUtils, SysUtils; -function TScreenEdit.ParseInput(PressedKey: cardinal; CharCode: WideChar; - PressedDown: boolean): boolean; +function TScreenEdit.ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; var SDL_ModState: word; begin @@ -75,8 +74,8 @@ begin if (PressedDown) then begin // Key Down // check normal keys - case WideCharUpperCase(CharCode)[1] of - 'Q': + case UCS4UpperCase(CharCode) of + Ord('Q'): begin Result := false; Exit; diff --git a/Lua/src/screens/UScreenEditConvert.pas b/Lua/src/screens/UScreenEditConvert.pas index 56afdefd..b2fb7773 100644 --- a/Lua/src/screens/UScreenEditConvert.pas +++ b/Lua/src/screens/UScreenEditConvert.pas @@ -25,6 +25,29 @@ unit UScreenEditConvert; +{* + * See + * MIDI Recommended Practice (RP-017): SMF Lyric Meta Event Definition + * http://www.midi.org/techspecs/rp17.php + * MIDI Recommended Practice (RP-026): SMF Language and Display Extensions + * http://www.midi.org/techspecs/rp26.php + * MIDI File Format + * http://www.sonicspot.com/guide/midifiles.html + * KMIDI File Format + * http://gnese.free.fr/Projects/KaraokeTime/Fichiers/karfaq.html + * http://journals.rpungin.fotki.com/karaoke/category/midi + * + * There are two widely spread karaoke formats: + * - KMIDI (.kar), an inofficial midi extension by Tune 1000 + * - Standard Midi files with lyric meta-tags (SMF with lyrics, .mid). + * + * KMIDI uses two tracks, the first just contains a header (mostly track 2) and + * the second the lyrics (track 3). It uses text meta tags for the lyrics. + * SMF uses just one track (normally track 1) and uses lyric meta tags for storage. + * + * Most files are in the KMIDI format. Some Midi files contain both lyric types. + *} + interface {$IFDEF FPC} @@ -34,6 +57,7 @@ interface {$I switches.inc} uses + math, UMenu, SDL, {$IFDEF UseMIDIPort} @@ -44,10 +68,11 @@ uses USongs, USong, UMusic, - UThemes; + UThemes, + UPath; type - TNote = record + TMidiNote = record Event: integer; EventType: integer; Channel: integer; @@ -55,88 +80,112 @@ type Len: real; Data1: integer; Data2: integer; - Str: string; + Str: UTF8String; // normally ASCII end; + TLyricType = (ltKMIDI, ltSMFLyric); + TTrack = record - Note: array of TNote; - Name: string; - Hear: boolean; - Status: set of (notes, lyrics); + Note: array of TMidiNote; + Name: UTF8String; // normally ASCII + Status: set of (tsNotes, tsLyrics); //< track contains notes, lyrics or both + LyricType: set of TLyricType; + NoteType: (ntNone, ntAvail); end; - TNuta = record + TNote = record Start: integer; Len: integer; Tone: integer; - Lyric: string; + Lyric: UTF8String; NewSentence: boolean; end; TArrayTrack = array of TTrack; TScreenEditConvert = class(TMenu) - public - ATrack: TArrayTrack; // actual track -// Track: TArrayTrack; - Channel: TArrayTrack; + private + Tracks: TArrayTrack; // current track ColR: array[0..100] of real; ColG: array[0..100] of real; ColB: array[0..100] of real; Len: real; - Sel: integer; - Selected: boolean; -// FileName: string; + SelTrack: integer; // index of selected track + fFileName: IPath; {$IFDEF UseMIDIPort} MidiFile: TMidiFile; - MidiTrack: TMidiTrack; - MidiEvent: pMidiEvent; MidiOut: TMidiOutput; {$ENDIF} - - Song: TSong; - Lines: TLines; + BPM: real; Ticks: real; - Note: array of TNuta; + Note: array of TNote; - procedure AddLyric(Start: integer; Text: string); - procedure Extract; + procedure AddLyric(Start: integer; LyricType: TLyricType; Text: UTF8String); + procedure Extract(out Song: TSong; out Lines: TLines); {$IFDEF UseMIDIPort} procedure MidiFile1MidiEvent(event: PMidiEvent); {$ENDIF} - - function SelectedNumber: integer; + + function CountSelectedTracks: integer; + + public constructor Create; override; - procedure onShow; override; - function ParseInput(PressedKey: cardinal; CharCode: WideChar; PressedDown: boolean): boolean; override; + procedure OnShow; override; + function ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; override; function Draw: boolean; override; - procedure onHide; override; + procedure OnHide; override; end; implementation uses - UGraphic, SysUtils, - UDrawTexture, TextGL, + gl, + UDrawTexture, UFiles, - UMain, + UGraphic, UIni, - gl, - USkins; - -function TScreenEditConvert.ParseInput(PressedKey: cardinal; CharCode: WideChar; PressedDown: boolean): boolean; + UMain, + UPathUtils, + USkins, + ULanguage, + UTextEncoding, + UUnicodeUtils; + +const + // MIDI/KAR lyrics are specified to be ASCII only. + // Assume backward compatible CP1252 encoding. + DEFAULT_ENCODING = encCP1252; + +const + MIDI_EVENTTYPE_NOTEOFF = $8; + MIDI_EVENTTYPE_NOTEON = $9; + MIDI_EVENTTYPE_META_SYSEX = $F; + + MIDI_EVENT_META = $FF; + MIDI_META_TEXT = $1; + MIDI_META_LYRICS = $5; + +function TScreenEditConvert.ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; +{$IFDEF UseMIDIPort} +var + SResult: TSaveSongResult; + Playing: boolean; + MidiTrack: TMidiTrack; + Song: TSong; + Lines: TLines; +{$ENDIF} begin Result := true; - If (PressedDown) Then + if (PressedDown) then begin // Key Down // check normal keys - case WideCharUpperCase(CharCode)[1] of - 'Q': + case UCS4UpperCase(CharCode) of + Ord('Q'): begin Result := false; Exit; @@ -148,9 +197,10 @@ begin SDLK_ESCAPE, SDLK_BACKSPACE : begin - {$IFDEF UseMIDIPort} - MidiFile.StopPlaying; - {$ENDIF} + {$IFDEF UseMIDIPort} + if (MidiFile <> nil) then + MidiFile.StopPlaying; + {$ENDIF} AudioPlayback.PlaySound(SoundLib.Back); FadeTo(@ScreenEdit); end; @@ -160,70 +210,97 @@ begin if Interaction = 0 then begin AudioPlayback.PlaySound(SoundLib.Start); + ScreenOpen.Filename := GamePath.Append('file.mid'); ScreenOpen.BackScreen := @ScreenEditConvert; FadeTo(@ScreenOpen); - end; - - if Interaction = 1 then + end + else if Interaction = 1 then begin - Selected := false; - {$IFDEF UseMIDIPort} - MidiFile.OnMidiEvent := MidiFile1MidiEvent; -// MidiFile.GoToTime(MidiFile.GetTrackLength div 2); - MidiFile.StartPlaying; - {$ENDIF} - end; - - if Interaction = 2 then - begin - Selected := true; {$IFDEF UseMIDIPort} - MidiFile.OnMidiEvent := nil; + if (MidiFile <> nil) then + begin + MidiFile.OnMidiEvent := MidiFile1MidiEvent; + //MidiFile.GoToTime(MidiFile.GetTrackLength div 2); + MidiFile.StartPlaying; + end; {$ENDIF} - {for T := 0 to High(ATrack) do - begin - if ATrack[T].Hear then - begin - MidiTrack := MidiFile.GetTrack(T); - MidiTrack.OnMidiEvent := MidiFile1MidiEvent; - end; + end + else if Interaction = 2 then + begin + {$IFDEF UseMIDIPort} + if (MidiFile <> nil) then + begin + MidiFile.OnMidiEvent := nil; + MidiFile.StartPlaying; end; - MidiFile.StartPlaying;//} - end; - - if Interaction = 3 then - begin - if SelectedNumber > 0 then - begin - Extract; - SaveSong(Song, Lines, ChangeFileExt(ConversionFileName, '.txt'), false); + {$ENDIF} + end + else if Interaction = 3 then + begin + {$IFDEF UseMIDIPort} + if CountSelectedTracks > 0 then + begin + Extract(Song, Lines); + SResult := SaveSong(Song, Lines, fFileName.SetExtension('.txt'), + false); + FreeAndNil(Song); + if (SResult = ssrOK) then + ScreenPopupInfo.ShowPopup(Language.Translate('INFO_FILE_SAVED')) + else + ScreenPopupError.ShowPopup(Language.Translate('ERROR_SAVE_FILE_FAILED')); + end + else + begin + ScreenPopupError.ShowPopup(Language.Translate('EDITOR_ERROR_NO_TRACK_SELECTED')); end; + {$ENDIF} end; end; SDLK_SPACE: begin -// ATrack[Sel].Hear := not ATrack[Sel].Hear; - if Notes in ATrack[Sel].Status then - begin - ATrack[Sel].Status := ATrack[Sel].Status - [Notes]; - if Lyrics in ATrack[Sel].Status then - ATrack[Sel].Status := ATrack[Sel].Status - [Lyrics] - else - ATrack[Sel].Status := ATrack[Sel].Status + [Lyrics]; - end - else - ATrack[Sel].Status := ATrack[Sel].Status + [Notes]; - -{ if Selected then + {$IFDEF UseMIDIPort} + if (MidiFile <> nil) then begin - MidiTrack := MidiFile.GetTrack(Sel); - if Track[Sel].Hear then + if (Tracks[SelTrack].NoteType = ntAvail) and + (Tracks[SelTrack].LyricType <> []) then + begin + if (Tracks[SelTrack].Status = []) then + Tracks[SelTrack].Status := [tsNotes] + else if (Tracks[SelTrack].Status = [tsNotes]) then + Tracks[SelTrack].Status := [tsLyrics] + else if (Tracks[SelTrack].Status = [tsLyrics]) then + Tracks[SelTrack].Status := [tsNotes, tsLyrics] + else if (Tracks[SelTrack].Status = [tsNotes, tsLyrics]) then + Tracks[SelTrack].Status := []; + end + else if (Tracks[SelTrack].NoteType = ntAvail) then + begin + if (Tracks[SelTrack].Status = []) then + Tracks[SelTrack].Status := [tsNotes] + else + Tracks[SelTrack].Status := []; + end + else if (Tracks[SelTrack].LyricType <> []) then + begin + if (Tracks[SelTrack].Status = []) then + Tracks[SelTrack].Status := [tsLyrics] + else + Tracks[SelTrack].Status := []; + end; + + Playing := (MidiFile.GetCurrentTime > 0); + MidiFile.StopPlaying(); + MidiTrack := MidiFile.GetTrack(SelTrack); + if tsNotes in Tracks[SelTrack].Status then MidiTrack.OnMidiEvent := MidiFile1MidiEvent else MidiTrack.OnMidiEvent := nil; - end;} + if (Playing) then + MidiFile.ContinuePlaying(); + end; + {$ENDIF} end; SDLK_RIGHT: @@ -238,102 +315,161 @@ begin SDLK_DOWN: begin - Inc(Sel); - if Sel > High(ATrack) then - Sel := 0; + Inc(SelTrack); + if SelTrack > High(Tracks) then + SelTrack := 0; end; SDLK_UP: begin - Dec(Sel); - if Sel < 0 then - Sel := High(ATrack); + Dec(SelTrack); + if SelTrack < 0 then + SelTrack := High(Tracks); end; end; end; end; -procedure TScreenEditConvert.AddLyric(Start: integer; Text: string); +procedure TScreenEditConvert.AddLyric(Start: integer; LyricType: TLyricType; Text: UTF8String); var N: integer; begin - for N := 0 to High(Note) do + // find corresponding note + N := 0; + while (N <= High(Note)) do begin if Note[N].Start = Start then - begin - // check for new sentece - if Copy(Text, 1, 1) = '\' then - Delete(Text, 1, 1); - if Copy(Text, 1, 1) = '/' then - begin - Delete(Text, 1, 1); - Note[N].NewSentence := true; - end; + Break; + Inc(N); + end; - // overwrite lyric od append - if Note[N].Lyric = '-' then - Note[N].Lyric := Text - else - Note[N].Lyric := Note[N].Lyric + Text; + // check if note was found + if (N > High(Note)) then + Exit; + + // set text + if (LyricType = ltKMIDI) then + begin + // end of paragraph + if Copy(Text, 1, 1) = '\' then + begin + Delete(Text, 1, 1); + end + // end of line + else if Copy(Text, 1, 1) = '/' then + begin + Delete(Text, 1, 1); + Note[N].NewSentence := true; + end; + end + else // SMFLyric + begin + // Line Feed -> end of paragraph + if Copy(Text, 1, 1) = #$0A then + begin + Delete(Text, 1, 1); + end + // Carriage Return -> end of line + else if Copy(Text, 1, 1) = #$0D then + begin + Delete(Text, 1, 1); + Note[N].NewSentence := true; end; end; + + // overwrite lyric or append + if Note[N].Lyric = '-' then + Note[N].Lyric := Text + else + Note[N].Lyric := Note[N].Lyric + Text; end; -procedure TScreenEditConvert.Extract; +procedure TScreenEditConvert.Extract(out Song: TSong; out Lines: TLines); + var T: integer; C: integer; N: integer; Nu: integer; - NoteTemp: TNuta; + NoteTemp: TNote; Move: integer; Max, Min: integer; + LyricType: TLyricType; + Text: UTF8String; begin // song info - Song.Title := ''; - Song.Artist := ''; - Song.Mp3 := ''; + Song := TSong.Create(); + Song.Clear(); Song.Resolution := 4; SetLength(Song.BPM, 1); Song.BPM[0].BPM := BPM*4; - SetLength(Note, 0); // extract notes - for T := 0 to High(ATrack) do + for T := 0 to High(Tracks) do begin -// if ATrack[T].Hear then -// begin - if Notes in ATrack[T].Status then + if tsNotes in Tracks[T].Status then begin - for N := 0 to High(ATrack[T].Note) do + for N := 0 to High(Tracks[T].Note) do begin - if (ATrack[T].Note[N].EventType = 9) and (ATrack[T].Note[N].Data2 > 0) then - begin + if (Tracks[T].Note[N].EventType = MIDI_EVENTTYPE_NOTEON) and + (Tracks[T].Note[N].Data2 > 0) then + begin Nu := Length(Note); SetLength(Note, Nu + 1); - Note[Nu].Start := Round(ATrack[T].Note[N].Start / Ticks); - Note[Nu].Len := Round(ATrack[T].Note[N].Len / Ticks); - Note[Nu].Tone := ATrack[T].Note[N].Data1 - 12*5; + Note[Nu].Start := Round(Tracks[T].Note[N].Start / Ticks); + Note[Nu].Len := Round(Tracks[T].Note[N].Len / Ticks); + Note[Nu].Tone := Tracks[T].Note[N].Data1 - 12*5; Note[Nu].Lyric := '-'; end; end; end; end; - // extract lyrics - for T := 0 to High(ATrack) do + // extract lyrics (and artist + title info) + for T := 0 to High(Tracks) do begin -// if ATrack[T].Hear then -// begin - if Lyrics in ATrack[T].Status then + if not (tsLyrics in Tracks[T].Status) then + Continue; + + for N := 0 to High(Tracks[T].Note) do begin - for N := 0 to High(ATrack[T].Note) do + if (Tracks[T].Note[N].Event = MIDI_EVENT_META) then begin - if (ATrack[T].Note[N].EventType = 15) then - begin -// Log.LogStatus('<' + Track[T].Note[N].Str + '>', 'MIDI'); - AddLyric(Round(ATrack[T].Note[N].Start / Ticks), ATrack[T].Note[N].Str); + // determine and validate lyric meta tag + if (ltKMIDI in Tracks[T].LyricType) and + (Tracks[T].Note[N].Data1 = MIDI_META_TEXT) then + begin + Text := Tracks[T].Note[N].Str; + + // check for meta info + if (Length(Text) > 2) and (Text[1] = '@') then + begin + case Text[2] of + 'L': Song.Language := Copy(Text, 3, Length(Text)); // language + 'T': begin // title info + if (Song.Artist = '') then + Song.Artist := Copy(Text, 3, Length(Text)) + else if (Song.Title = '') then + Song.Title := Copy(Text, 3, Length(Text)); + end; + end; + Continue; + end; + + LyricType := ltKMIDI; + end + else if (ltSMFLyric in Tracks[T].LyricType) and + (Tracks[T].Note[N].Data1 = MIDI_META_LYRICS) then + begin + LyricType := ltSMFLyric; + end + else + begin + // unknown meta event + Continue; end; + + AddLyric(Round(Tracks[T].Note[N].Start / Ticks), LyricType, Tracks[T].Note[N].Str); end; end; end; @@ -355,8 +491,12 @@ begin // copy notes SetLength(Lines.Line, 1); - Lines.Number := 1; - Lines.High := 0; + Lines.Number := 1; + Lines.High := 0; + Lines.Current := 0; + Lines.Resolution := 0; + Lines.NotesGAP := 0; + Lines.ScoreValue := 0; C := 0; N := 0; @@ -398,35 +538,37 @@ begin // create space for new note SetLength(Lines.Line[C].Note, Length(Lines.Line[C].Note)+1); + Inc(Lines.Line[C].HighNote); // initialize note Lines.Line[C].Note[N].Start := Note[Nu].Start; Lines.Line[C].Note[N].Length := Note[Nu].Len; Lines.Line[C].Note[N].Tone := Note[Nu].Tone; - Lines.Line[C].Note[N].Text := Note[Nu].Lyric; - //All Notes are Freestyle when Converted Fix: + Lines.Line[C].Note[N].Text := DecodeStringUTF8(Note[Nu].Lyric, DEFAULT_ENCODING); Lines.Line[C].Note[N].NoteType := ntNormal; Inc(N); end; end; -function TScreenEditConvert.SelectedNumber: integer; +function TScreenEditConvert.CountSelectedTracks: integer; var T: integer; // track begin Result := 0; - for T := 0 to High(ATrack) do -// if ATrack[T].Hear then -// Inc(Result); - if Notes in ATrack[T].Status then + for T := 0 to High(Tracks) do + if tsNotes in Tracks[T].Status then Inc(Result); end; {$IFDEF UseMIDIPort} procedure TScreenEditConvert.MidiFile1MidiEvent(event: PMidiEvent); begin -// Log.LogStatus(IntToStr(event.event), 'MIDI'); - MidiOut.PutShort(event.event, event.data1, event.data2); + //Log.LogStatus(IntToStr(event.event), 'MIDI'); + try + MidiOut.PutShort(event.event, event.data1, event.data2); + except + MidiFile.StopPlaying(); + end; end; {$ENDIF} @@ -437,7 +579,7 @@ begin inherited Create; AddButton(40, 20, 100, 40, Skin.GetTextureFileName('ButtonF')); AddButtonText(15, 5, 0, 0, 0, 'Open'); -// Button[High(Button)].Text[0].Size := 11; + //Button[High(Button)].Text[0].Size := 11; AddButton(160, 20, 100, 40, Skin.GetTextureFileName('ButtonF')); AddButtonText(25, 5, 0, 0, 0, 'Play'); @@ -448,20 +590,7 @@ begin AddButton(500, 20, 100, 40, Skin.GetTextureFileName('ButtonF')); AddButtonText(20, 5, 0, 0, 0, 'Save'); - -{ MidiOut := TMidiOutput.Create(nil); -// MidiOut.Close; -// MidiOut.DeviceID := 0; - if Ini.Debug = 1 then - MidiOut.ProductName := 'Microsoft GS Wavetable SW Synth'; // for my kxproject without midi table - Log.LogStatus(MidiOut.ProductName, 'MIDI'); - MidiOut.Open; -// MidiOut.SetVolume(100, 100); // temporary} - - ConversionFileName := GamePath + 'file.mid'; - {$IFDEF UseMIDIPort} - MidiFile := TMidiFile.Create(nil); - {$ENDIF} + fFileName := PATH_NONE; for P := 0 to 100 do begin @@ -472,98 +601,124 @@ begin end; -procedure TScreenEditConvert.onShow; +procedure TScreenEditConvert.OnShow; var T: integer; // track N: integer; // note - C: integer; // channel - CN: integer; // channel note + {$IFDEF UseMIDIPort} + MidiTrack: TMidiTrack; + MidiEvent: PMidiEvent; + {$ENDIF} + FileOpened: boolean; + KMIDITrackIndex, SMFTrackIndex: integer; begin inherited; + Interaction := 0; + {$IFDEF UseMIDIPort} MidiOut := TMidiOutput.Create(nil); - if Ini.Debug = 1 then - MidiOut.ProductName := 'Microsoft GS Wavetable SW Synth'; // for my kxproject without midi table - Log.LogStatus(MidiOut.ProductName, 'MIDI'); + Log.LogInfo(MidiOut.ProductName, 'MIDI'); MidiOut.Open; + MidiFile := nil; + SetLength(Tracks, 0); + // Filename is only <> PATH_NONE if we called the OpenScreen before + fFilename := ScreenOpen.Filename; + if (fFilename = PATH_NONE) then + Exit; + ScreenOpen.Filename := PATH_NONE; - if FileExists(ConversionFileName) then + FileOpened := false; + if fFileName.Exists then begin - MidiFile.Filename := ConversionFileName; - MidiFile.ReadFile; - + MidiFile := TMidiFile.Create(nil); + MidiFile.Filename := fFileName; + try + MidiFile.ReadFile; + FileOpened := true; + except + MidiFile.Free; + end; + end; - Len := 0; - Sel := 0; - BPM := MidiFile.Bpm; - Ticks := MidiFile.TicksPerQuarter / 4; + if (not FileOpened) then + begin + ScreenPopupError.ShowPopup(Language.Translate('ERROR_FILE_NOT_FOUND')); + Exit; + end; -{ for T := 0 to MidiFile.NumberOfTracks-1 do - begin - SetLength(Track, Length(Track)+1); - MidiTrack := MidiFile.GetTrack(T); - MidiTrack.OnMidiEvent := MidiFile1MidiEvent; - Track[T].Name := MidiTrack.getName; + Len := 0; + SelTrack := 0; + BPM := MidiFile.Bpm; + Ticks := MidiFile.TicksPerQuarter / 4; - for N := 0 to MidiTrack.getEventCount-1 do - begin - SetLength(Track[T].Note, Length(Track[T].Note)+1); - MidiEvent := MidiTrack.GetEvent(N); - Track[T].Note[N].Start := MidiEvent.time; - Track[T].Note[N].Len := MidiEvent.len; - Track[T].Note[N].Event := MidiEvent.event; - Track[T].Note[N].EventType := MidiEvent.event div 16; - Track[T].Note[N].Channel := MidiEvent.event and 15; - Track[T].Note[N].Data1 := MidiEvent.data1; - Track[T].Note[N].Data2 := MidiEvent.data2; - Track[T].Note[N].Str := MidiEvent.str; - - if Track[T].Note[N].Start + Track[T].Note[N].Len > Len then - Len := Track[T].Note[N].Start + Track[T].Note[N].Len; - end; - end;} + KMIDITrackIndex := -1; + SMFTrackIndex := -1; - SetLength(Channel, 16); - for T := 0 to 15 do - begin - Channel[T].Name := IntToStr(T+1); - SetLength(Channel[T].Note, 0); - Channel[T].Status := []; - end; + SetLength(Tracks, MidiFile.NumberOfTracks); + for T := 0 to MidiFile.NumberOfTracks-1 do + Tracks[T].LyricType := []; - for T := 0 to MidiFile.NumberOfTracks-1 do + for T := 0 to MidiFile.NumberOfTracks-1 do + begin + MidiTrack := MidiFile.GetTrack(T); + MidiTrack.OnMidiEvent := nil; + Tracks[T].Name := DecodeStringUTF8(MidiTrack.getName, DEFAULT_ENCODING); + Tracks[T].NoteType := ntNone; + Tracks[T].Status := []; + + SetLength(Tracks[T].Note, MidiTrack.getEventCount()); + for N := 0 to MidiTrack.getEventCount-1 do begin - MidiTrack := MidiFile.GetTrack(T); - MidiTrack.OnMidiEvent := MidiFile1MidiEvent; - - for N := 0 to MidiTrack.getEventCount-1 do + MidiEvent := MidiTrack.GetEvent(N); + + Tracks[T].Note[N].Start := MidiEvent.time; + Tracks[T].Note[N].Len := MidiEvent.len; + Tracks[T].Note[N].Event := MidiEvent.event; + Tracks[T].Note[N].EventType := MidiEvent.event shr 4; + Tracks[T].Note[N].Channel := MidiEvent.event and $0F; + Tracks[T].Note[N].Data1 := MidiEvent.data1; + Tracks[T].Note[N].Data2 := MidiEvent.data2; + Tracks[T].Note[N].Str := DecodeStringUTF8(MidiEvent.str, DEFAULT_ENCODING); + + if (Tracks[T].Note[N].Event = MIDI_EVENT_META) then begin - MidiEvent := MidiTrack.GetEvent(N); - C := MidiEvent.event and 15; - - CN := Length(Channel[C].Note); - SetLength(Channel[C].Note, CN+1); - - Channel[C].Note[CN].Start := MidiEvent.time; - Channel[C].Note[CN].Len := MidiEvent.len; - Channel[C].Note[CN].Event := MidiEvent.event; - Channel[C].Note[CN].EventType := MidiEvent.event div 16; - Channel[C].Note[CN].Channel := MidiEvent.event and 15; - Channel[C].Note[CN].Data1 := MidiEvent.data1; - Channel[C].Note[CN].Data2 := MidiEvent.data2; - Channel[C].Note[CN].Str := MidiEvent.str; - - if Channel[C].Note[CN].Start + Channel[C].Note[CN].Len > Len then - Len := Channel[C].Note[CN].Start + Channel[C].Note[CN].Len; + case (Tracks[T].Note[N].Data1) of + MIDI_META_TEXT: begin + // KMIDI lyrics (uses MIDI_META_TEXT events) + if (StrLComp(PAnsiChar(Tracks[T].Note[N].Str), '@KMIDI KARAOKE FILE', 19) = 0) and + (High(Tracks) >= T+1) then + begin + // The '@KMIDI ...' mark is in the first track (mostly named 'Soft Karaoke') + // but the lyrics are in the second track (named 'Words') + Tracks[T+1].LyricType := Tracks[T+1].LyricType + [ltKMIDI]; + KMIDITrackIndex := T+1; + end; + end; + MIDI_META_LYRICS: begin + // lyrics in Standard Midi File format found (uses MIDI_META_LYRICS events) + Tracks[T].LyricType := Tracks[T].LyricType + [ltSMFLyric]; + SMFTrackIndex := T; + end; + end; + end + else if (Tracks[T].Note[N].EventType = MIDI_EVENTTYPE_NOTEON) then + begin + // notes available + Tracks[T].NoteType := ntAvail; end; - end; - ATrack := Channel; + if Tracks[T].Note[N].Start + Tracks[T].Note[N].Len > Len then + Len := Tracks[T].Note[N].Start + Tracks[T].Note[N].Len; + end; end; - Interaction := 0; + // set default lyric track. Prefer KMIDI. + if (KMIDITrackIndex > -1) then + Tracks[KMIDITrackIndex].Status := Tracks[KMIDITrackIndex].Status + [tsLyrics] + else if (SMFTrackIndex > -1) then + Tracks[SMFTrackIndex].Status := Tracks[SMFTrackIndex].Status + [tsLyrics]; {$ENDIF} end; @@ -574,82 +729,98 @@ var Bottom: real; X: real; Y: real; - H: real; + Height: real; YSkip: real; + TrackName: UTF8String; begin // draw static menu inherited Draw; Y := 100; - H := Length(ATrack)*40; - if H > 480 then - H := 480; - Bottom := Y + H; + Height := min(480, 40 * Length(Tracks)); + Bottom := Y + Height; - YSkip := H / Length(ATrack); + YSkip := Height / Length(Tracks); - // select - DrawQuad(10, Y+Sel*YSkip, 780, YSkip, 0.8, 0.8, 0.8); + // highlight selected track + DrawQuad(10, Y+SelTrack*YSkip, 780, YSkip, 0.8, 0.8, 0.8); - // selected - now me use Status System - for Count := 0 to High(ATrack) do - if ATrack[Count].Hear then - DrawQuad(10, Y+Count*YSkip, 50, YSkip, 0.8, 0.3, 0.3); + // track-selection info + for Count := 0 to High(Tracks) do + if Tracks[Count].Status <> [] then + DrawQuad(10, Y + Count*YSkip, 50, YSkip, 0.8, 0.3, 0.3); glColor3f(0, 0, 0); - for Count := 0 to High(ATrack) do + for Count := 0 to High(Tracks) do begin - if Notes in ATrack[Count].Status then + if Tracks[Count].NoteType = ntAvail then begin + if tsNotes in Tracks[Count].Status then + glColor3f(0, 0, 0) + else + glColor3f(0.7, 0.7, 0.7); SetFontPos(25, Y + Count*YSkip + 10); SetFontSize(15); glPrint('N'); end; - if Lyrics in ATrack[Count].Status then + if Tracks[Count].LyricType <> [] then begin + if tsLyrics in Tracks[Count].Status then + glColor3f(0, 0, 0) + else + glColor3f(0.7, 0.7, 0.7); SetFontPos(40, Y + Count*YSkip + 10); SetFontSize(15); glPrint('L'); end; end; - DrawLine(10, Y, 10, Bottom, 0, 0, 0); - DrawLine(60, Y, 60, Bottom, 0, 0, 0); + DrawLine( 10, Y, 10, Bottom, 0, 0, 0); + DrawLine( 60, Y, 60, Bottom, 0, 0, 0); DrawLine(790, Y, 790, Bottom, 0, 0, 0); - for Count := 0 to Length(ATrack) do - DrawLine(10, Y+Count*YSkip, 790, Y+Count*YSkip, 0, 0, 0); + for Count := 0 to Length(Tracks) do + DrawLine(10, Y + Count*YSkip, 790, Y + Count*YSkip, 0, 0, 0); - for Count := 0 to High(ATrack) do + for Count := 0 to High(Tracks) do begin - SetFontPos(11, Y + 10 + Count*YSkip); + SetFontPos(65, Y + Count*YSkip); SetFontSize(15); - glPrint(ATrack[Count].Name); + glPrint(Tracks[Count].Name); end; - for Count := 0 to High(ATrack) do - for Count2 := 0 to High(ATrack[Count].Note) do + for Count := 0 to High(Tracks) do + begin + for Count2 := 0 to High(Tracks[Count].Note) do begin - if ATrack[Count].Note[Count2].EventType = 9 then - DrawQuad(60 + ATrack[Count].Note[Count2].Start/Len * 725, Y + (Count+1)*YSkip - ATrack[Count].Note[Count2].Data1*35/127, 3, 3, ColR[Count], ColG[Count], ColB[Count]); - if ATrack[Count].Note[Count2].EventType = 15 then - DrawLine(60 + ATrack[Count].Note[Count2].Start/Len * 725, Y + 0.75 * YSkip + Count*YSkip, 60 + ATrack[Count].Note[Count2].Start/Len * 725, Y + YSkip + Count*YSkip, ColR[Count], ColG[Count], ColB[Count]); + if Tracks[Count].Note[Count2].EventType = MIDI_EVENTTYPE_NOTEON then + DrawQuad(60 + Tracks[Count].Note[Count2].Start/Len * 725, + Y + (Count+1)*YSkip - Tracks[Count].Note[Count2].Data1*35/127, + 3, 3, + ColR[Count], ColG[Count], ColB[Count]); + if Tracks[Count].Note[Count2].EventType = 15 then + DrawLine(60 + Tracks[Count].Note[Count2].Start/Len * 725, Y + 0.75 * YSkip + Count*YSkip, + 60 + Tracks[Count].Note[Count2].Start/Len * 725, Y + YSkip + Count*YSkip, + ColR[Count], ColG[Count], ColB[Count]); end; + end; // playing line {$IFDEF UseMIDIPort} - X := 60 + MidiFile.GetCurrentTime/MidiFile.GetTrackLength*730; + if (MidiFile <> nil) then + X := 60 + MidiFile.GetCurrentTime/MidiFile.GetTrackLength*730; {$ENDIF} DrawLine(X, Y, X, Bottom, 0.3, 0.3, 0.3); Result := true; end; -procedure TScreenEditConvert.onHide; +procedure TScreenEditConvert.OnHide; begin {$IFDEF UseMIDIPort} + FreeAndNil(MidiFile); MidiOut.Close; - MidiOut.Free; + FreeAndNil(MidiOut); {$ENDIF} end; diff --git a/Lua/src/screens/UScreenEditHeader.pas b/Lua/src/screens/UScreenEditHeader.pas index fb0b2c85..c581215b 100644 --- a/Lua/src/screens/UScreenEditHeader.pas +++ b/Lua/src/screens/UScreenEditHeader.pas @@ -38,6 +38,7 @@ uses SDL, USongs, USong, + UPath, UThemes; type @@ -72,8 +73,8 @@ type procedure SetRoundButtons; constructor Create; override; - procedure onShow; override; - function ParseInput(PressedKey: cardinal; CharCode: WideChar; PressedDown: boolean): boolean; override; + procedure OnShow; override; + function ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; override; { function Draw: boolean; override; procedure Finish;} end; @@ -86,17 +87,18 @@ uses SysUtils, UFiles, USkins, - UTexture; + UTexture, + UUnicodeUtils; -function TScreenEditHeader.ParseInput(PressedKey: cardinal; CharCode: WideChar; PressedDown: boolean): boolean; +function TScreenEditHeader.ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; var T: integer; begin Result := true; if (PressedDown) then // Key Down begin // check normal keys - case WideCharUpperCase(CharCode)[1] of - 'Q': + case UCS4UpperCase(CharCode) of + Ord('Q'): begin Result := false; Exit; @@ -105,18 +107,18 @@ begin // check special keys case PressedKey of - SDLK_ESCAPE : + SDLK_ESCAPE: begin -// Music.PlayBack; -// FadeTo(@MainScreen); + //Music.PlayBack; + //FadeTo(@MainScreen); Result := false; end; SDLK_RETURN: begin if Interaction = 1 then - begin -// Save; + begin + //Save; end; end; @@ -158,20 +160,20 @@ begin begin T := Interaction - 2 + TextTitle; if (Interaction >= 2) and (Interaction <= 13) and (Length(Text[T].Text) >= 1) then - begin - Text[T].DeleteLastL; + begin + Text[T].DeleteLastLetter; SetRoundButtons; end; end; end; case CharCode of - #32..#255: + 32..255: begin if (Interaction >= 2) and (Interaction <= 13) then - begin + begin Text[Interaction - 2 + TextTitle].Text := - Text[Interaction - 2 + TextTitle].Text + CharCode; + Text[Interaction - 2 + TextTitle].Text + UCS4ToUTF8String(CharCode); SetRoundButtons; end; end; @@ -223,18 +225,18 @@ begin TextGAP := AddText(340, 110 + 13*30, 0, 30, 0, 0, 0, ''); TextBPM := AddText(340, 110 + 14*30, 0, 30, 0, 0, 0, ''); - StaticTitle := AddStatic(130, 115 + 0*30, 20, 20, 1, 1, 1, 'RoundButton', TEXTURE_TYPE_TRANSPARENT, $FF00FF); - StaticArtist := AddStatic(130, 115 + 1*30, 20, 20, 1, 1, 1, 'RoundButton', TEXTURE_TYPE_TRANSPARENT, $FF00FF); - StaticMp3 := AddStatic(130, 115 + 2*30, 20, 20, 1, 1, 1, 'RoundButton', TEXTURE_TYPE_TRANSPARENT, $FF00FF); - StaticBackground := AddStatic(130, 115 + 4*30, 20, 20, 1, 1, 1, 'RoundButton', TEXTURE_TYPE_TRANSPARENT, $FF00FF); - StaticVideo := AddStatic(130, 115 + 5*30, 20, 20, 1, 1, 1, 'RoundButton', TEXTURE_TYPE_TRANSPARENT, $FF00FF); - StaticVideoGAP := AddStatic(130, 115 + 6*30, 20, 20, 1, 1, 1, 'RoundButton', TEXTURE_TYPE_TRANSPARENT, $FF00FF); - StaticRelative := AddStatic(130, 115 + 8*30, 20, 20, 1, 1, 1, 'RoundButton', TEXTURE_TYPE_TRANSPARENT, $FF00FF); - StaticResolution := AddStatic(130, 115 + 9*30, 20, 20, 1, 1, 1, 'RoundButton', TEXTURE_TYPE_TRANSPARENT, $FF00FF); - StaticNotesGAP := AddStatic(130, 115 + 10*30, 20, 20, 1, 1, 1, 'RoundButton', TEXTURE_TYPE_TRANSPARENT, $FF00FF); - StaticStart := AddStatic(130, 115 + 12*30, 20, 20, 1, 1, 1, 'RoundButton', TEXTURE_TYPE_TRANSPARENT, $FF00FF); - StaticGAP := AddStatic(130, 115 + 13*30, 20, 20, 1, 1, 1, 'RoundButton', TEXTURE_TYPE_TRANSPARENT, $FF00FF); - StaticBPM := AddStatic(130, 115 + 14*30, 20, 20, 1, 1, 1, 'RoundButton', TEXTURE_TYPE_TRANSPARENT, $FF00FF); + StaticTitle := AddStatic(130, 115 + 0*30, 20, 20, 1, 1, 1, Path('RoundButton'), TEXTURE_TYPE_TRANSPARENT, $FF00FF); + StaticArtist := AddStatic(130, 115 + 1*30, 20, 20, 1, 1, 1, Path('RoundButton'), TEXTURE_TYPE_TRANSPARENT, $FF00FF); + StaticMp3 := AddStatic(130, 115 + 2*30, 20, 20, 1, 1, 1, Path('RoundButton'), TEXTURE_TYPE_TRANSPARENT, $FF00FF); + StaticBackground := AddStatic(130, 115 + 4*30, 20, 20, 1, 1, 1, Path('RoundButton'), TEXTURE_TYPE_TRANSPARENT, $FF00FF); + StaticVideo := AddStatic(130, 115 + 5*30, 20, 20, 1, 1, 1, Path('RoundButton'), TEXTURE_TYPE_TRANSPARENT, $FF00FF); + StaticVideoGAP := AddStatic(130, 115 + 6*30, 20, 20, 1, 1, 1, Path('RoundButton'), TEXTURE_TYPE_TRANSPARENT, $FF00FF); + StaticRelative := AddStatic(130, 115 + 8*30, 20, 20, 1, 1, 1, Path('RoundButton'), TEXTURE_TYPE_TRANSPARENT, $FF00FF); + StaticResolution := AddStatic(130, 115 + 9*30, 20, 20, 1, 1, 1, Path('RoundButton'), TEXTURE_TYPE_TRANSPARENT, $FF00FF); + StaticNotesGAP := AddStatic(130, 115 + 10*30, 20, 20, 1, 1, 1, Path('RoundButton'), TEXTURE_TYPE_TRANSPARENT, $FF00FF); + StaticStart := AddStatic(130, 115 + 12*30, 20, 20, 1, 1, 1, Path('RoundButton'), TEXTURE_TYPE_TRANSPARENT, $FF00FF); + StaticGAP := AddStatic(130, 115 + 13*30, 20, 20, 1, 1, 1, Path('RoundButton'), TEXTURE_TYPE_TRANSPARENT, $FF00FF); + StaticBPM := AddStatic(130, 115 + 14*30, 20, 20, 1, 1, 1, Path('RoundButton'), TEXTURE_TYPE_TRANSPARENT, $FF00FF); AddInteraction(iText, TextTitle); AddInteraction(iText, TextArtist); @@ -250,7 +252,7 @@ begin AddInteraction(iText, TextBPM); end; -procedure TScreenEditHeader.onShow; +procedure TScreenEditHeader.OnShow; begin inherited; diff --git a/Lua/src/screens/UScreenEditSub.pas b/Lua/src/screens/UScreenEditSub.pas index 07113363..609a689b 100644 --- a/Lua/src/screens/UScreenEditSub.pas +++ b/Lua/src/screens/UScreenEditSub.pas @@ -116,11 +116,11 @@ type Tex_Background: TTexture; FadeOut: boolean; constructor Create; override; - procedure onShow; override; - function ParseInput(PressedKey: cardinal; CharCode: WideChar; PressedDown: boolean): boolean; override; - function ParseInputEditText(PressedKey: cardinal; CharCode: WideChar; PressedDown: boolean): boolean; + procedure OnShow; override; + function ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; override; + function ParseInputEditText(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; function Draw: boolean; override; - procedure onHide; override; + procedure OnHide; override; end; implementation @@ -128,16 +128,46 @@ implementation uses UGraphic, UDraw, - UMain, + UNote, USkins, - ULanguage; + ULanguage, + UTextEncoding, + UUnicodeUtils, + UPath; -// Method for input parsing. If False is returned, GetNextWindow + +procedure OnSaveEncodingError(Value: boolean; Data: Pointer); +var + SResult: TSaveSongResult; + FilePath: IPath; + Success: boolean; +begin + Success := false; + if (Value) then + begin + CurrentSong.Encoding := encUTF8; + FilePath := CurrentSong.Path.Append(CurrentSong.FileName); + // create backup file + FilePath.CopyFile(Path(FilePath.ToUTF8 + '.ansi.bak'), false); + // store in UTF-8 encoding + SResult := SaveSong(CurrentSong, Lines[0], FilePath, + boolean(Data)); + Success := (SResult = ssrOK); + end; + + if (Success) then + ScreenPopupInfo.ShowPopup(Language.Translate('INFO_FILE_SAVED')) + else + ScreenPopupError.ShowPopup(Language.Translate('ERROR_SAVE_FILE_FAILED')); +end; + +// Method for input parsing. If false is returned, GetNextWindow // should be checked to know the next window to load; -function TScreenEditSub.ParseInput(PressedKey: cardinal; CharCode: WideChar; PressedDown: boolean): boolean; +function TScreenEditSub.ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; var - SDL_ModState: Word; + SDL_ModState: word; R: real; + SResult: TSaveSongResult; begin Result := true; @@ -152,40 +182,47 @@ begin + KMOD_LCTRL + KMOD_RCTRL + KMOD_LALT + KMOD_RALT {+ KMOD_CAPS}); if (PressedDown) then // Key Down - begin // check normal keys - case WideCharUpperCase(CharCode)[1] of - 'Q': + begin + // check normal keys + case UCS4UpperCase(CharCode) of + Ord('Q'): begin Result := false; Exit; end; - 'S': + Ord('S'): begin // Save Song - if SDL_ModState = KMOD_LSHIFT then - SaveSong(CurrentSong, Lines[0], CurrentSong.Path + CurrentSong.FileName, true) + SResult := SaveSong(CurrentSong, Lines[0], CurrentSong.Path.Append(CurrentSong.FileName), + (SDL_ModState = KMOD_LSHIFT)); + if (SResult = ssrOK) then + begin + ScreenPopupInfo.ShowPopup(Language.Translate('INFO_FILE_SAVED')); + end + else if (SResult = ssrEncodingError) then + begin + ScreenPopupCheck.ShowPopup(Language.Translate('ENCODING_ERROR_ASK_FOR_UTF8'), OnSaveEncodingError, + Pointer(SDL_ModState = KMOD_LSHIFT), true); + end else - SaveSong(CurrentSong, Lines[0], CurrentSong.Path + CurrentSong.FileName, false); - - {if SDL_ModState = KMOD_LSHIFT or KMOD_LCTRL + KMOD_LALT then - // Save Song - SaveSongDebug(CurrentSong, Lines[0], 'C:\song.asm', false);} - + begin + ScreenPopupError.ShowPopup(Language.Translate('ERROR_SAVE_FILE_FAILED')); + end; Exit; end; - 'D': + Ord('D'): begin // Divide lengths by 2 DivideBPM; Exit; end; - 'M': + Ord('M'): begin // Multiply lengths by 2 MultiplyBPM; Exit; end; - 'C': + Ord('C'): begin // Capitalize letter at the beginning of line if SDL_ModState = 0 then @@ -201,11 +238,11 @@ begin Exit; end; - 'V': + Ord('V'): begin // Paste text if SDL_ModState = KMOD_LCTRL then - begin + begin if Lines[0].Line[Lines[0].Current].HighNote >= Lines[0].Line[CopySrc].HighNote then PasteText else @@ -213,17 +250,17 @@ begin end; if SDL_ModState = KMOD_LCTRL + KMOD_LSHIFT then - begin + begin CopySentence(CopySrc, Lines[0].Current); end; end; - 'T': + Ord('T'): begin // Fixes timings between sentences FixTimings; Exit; end; - 'P': + Ord('P'): begin if SDL_ModState = 0 then begin @@ -269,8 +306,8 @@ begin Exit; end; - // Golden Note Patch - 'G': + // Golden Note + Ord('G'): begin if (Lines[0].Line[Lines[0].Current].Note[CurrentNote].NoteType = ntGolden) then Lines[0].Line[Lines[0].Current].Note[CurrentNote].NoteType := ntNormal @@ -280,8 +317,8 @@ begin Exit; end; - // Freestyle Note Patch - 'F': + // Freestyle Note + Ord('F'): begin if (Lines[0].Line[Lines[0].Current].Note[CurrentNote].NoteType = ntFreestyle) then Lines[0].Line[Lines[0].Current].Note[CurrentNote].NoteType := ntNormal @@ -333,7 +370,7 @@ begin SDLK_4: begin if SDL_ModState = KMOD_LCTRL + KMOD_LSHIFT then - begin + begin CopySentence(CopySrc, Lines[0].Current); CopySentence(CopySrc+1, Lines[0].Current+1); CopySentence(CopySrc+2, Lines[0].Current+2); @@ -341,14 +378,14 @@ begin end; if SDL_ModState = KMOD_LCTRL + KMOD_LSHIFT + KMOD_LALT then - begin + begin CopySentences(CopySrc, Lines[0].Current, 4); end; end; SDLK_5: begin if SDL_ModState = KMOD_LCTRL + KMOD_LSHIFT then - begin + begin CopySentence(CopySrc, Lines[0].Current); CopySentence(CopySrc+1, Lines[0].Current+1); CopySentence(CopySrc+2, Lines[0].Current+2); @@ -357,7 +394,7 @@ begin end; if SDL_ModState = KMOD_LCTRL + KMOD_LSHIFT + KMOD_LALT then - begin + begin CopySentences(CopySrc, Lines[0].Current, 5); end; end; @@ -400,21 +437,21 @@ begin SDLK_SLASH: begin if SDL_ModState = 0 then - begin + begin // Insert start of sentece if CurrentNote > 0 then DivideSentence; end; if SDL_ModState = KMOD_LSHIFT then - begin + begin // Join next sentence with current if Lines[0].Current < Lines[0].High then JoinSentence; end; if SDL_ModState = KMOD_LCTRL then - begin + begin // divide note DivideNote; end; @@ -449,7 +486,7 @@ begin SDLK_DELETE: begin if SDL_ModState = KMOD_LCTRL then - begin + begin // moves text to right in current sentence DeleteNote; end; @@ -465,24 +502,24 @@ begin begin // right if SDL_ModState = 0 then - begin - Lines[0].Line[Lines[0].Current].Note[CurrentNote].Color := 0; + begin + Lines[0].Line[Lines[0].Current].Note[CurrentNote].Color := 1; Inc(CurrentNote); if CurrentNote > Lines[0].Line[Lines[0].Current].HighNote then - CurrentNote := 0; - Lines[0].Line[Lines[0].Current].Note[CurrentNote].Color := 1; + CurrentNote := 0; + Lines[0].Line[Lines[0].Current].Note[CurrentNote].Color := 2; Lyric.Selected := CurrentNote; end; // ctrl + right if SDL_ModState = KMOD_LCTRL then - begin + begin if Lines[0].Line[Lines[0].Current].Note[CurrentNote].Length > 1 then - begin + begin Dec(Lines[0].Line[Lines[0].Current].Note[CurrentNote].Length); Inc(Lines[0].Line[Lines[0].Current].Note[CurrentNote].Start); if CurrentNote = 0 then - begin + begin Inc(Lines[0].Line[Lines[0].Current].Start); end; end; @@ -490,10 +527,10 @@ begin // shift + right if SDL_ModState = KMOD_LSHIFT then - begin + begin Inc(Lines[0].Line[Lines[0].Current].Note[CurrentNote].Start); if CurrentNote = 0 then - begin + begin Inc(Lines[0].Line[Lines[0].Current].Start); end; if CurrentNote = Lines[0].Line[Lines[0].Current].HighNote then @@ -502,7 +539,7 @@ begin // alt + right if SDL_ModState = KMOD_LALT then - begin + begin Inc(Lines[0].Line[Lines[0].Current].Note[CurrentNote].Length); if CurrentNote = Lines[0].Line[Lines[0].Current].HighNote then Inc(Lines[0].Line[Lines[0].Current].End_); @@ -510,7 +547,7 @@ begin // alt + ctrl + shift + right = move all from cursor to right if SDL_ModState = KMOD_LALT + KMOD_LCTRL + KMOD_LSHIFT then - begin + begin MoveAllToEnd(1); end; @@ -520,34 +557,34 @@ begin begin // left if SDL_ModState = 0 then - begin - Lines[0].Line[Lines[0].Current].Note[CurrentNote].Color := 0; + begin + Lines[0].Line[Lines[0].Current].Note[CurrentNote].Color := 1; Dec(CurrentNote); if CurrentNote = -1 then - CurrentNote := Lines[0].Line[Lines[0].Current].HighNote; - Lines[0].Line[Lines[0].Current].Note[CurrentNote].Color := 1; + CurrentNote := Lines[0].Line[Lines[0].Current].HighNote; + Lines[0].Line[Lines[0].Current].Note[CurrentNote].Color := 2; Lyric.Selected := CurrentNote; end; // ctrl + left if SDL_ModState = KMOD_LCTRL then - begin + begin Dec(Lines[0].Line[Lines[0].Current].Note[CurrentNote].Start); Inc(Lines[0].Line[Lines[0].Current].Note[CurrentNote].Length); if CurrentNote = 0 then - begin + begin Dec(Lines[0].Line[Lines[0].Current].Start); end; end; // shift + left if SDL_ModState = KMOD_LSHIFT then - begin + begin Dec(Lines[0].Line[Lines[0].Current].Note[CurrentNote].Start); // resizing sentences if CurrentNote = 0 then - begin + begin Dec(Lines[0].Line[Lines[0].Current].Start); end; @@ -558,9 +595,9 @@ begin // alt + left if SDL_ModState = KMOD_LALT then - begin + begin if Lines[0].Line[Lines[0].Current].Note[CurrentNote].Length > 1 then - begin + begin Dec(Lines[0].Line[Lines[0].Current].Note[CurrentNote].Length); if CurrentNote = Lines[0].Line[Lines[0].Current].HighNote then Dec(Lines[0].Line[Lines[0].Current].End_); @@ -569,7 +606,7 @@ begin // alt + ctrl + shift + right = move all from cursor to left if SDL_ModState = KMOD_LALT + KMOD_LCTRL + KMOD_LSHIFT then - begin + begin MoveAllToEnd(-1); end; @@ -580,17 +617,18 @@ begin // skip to next sentence if SDL_ModState = 0 then - begin {$IFDEF UseMIDIPort} + begin + {$IFDEF UseMIDIPort} MidiOut.PutShort($81, Lines[0].Line[Lines[0].Current].Note[MidiLastNote].Tone + 60, 127); PlaySentenceMidi := false; - {$endif} + {$ENDIF} - Lines[0].Line[Lines[0].Current].Note[CurrentNote].Color := 0; + Lines[0].Line[Lines[0].Current].Note[CurrentNote].Color := 1; Inc(Lines[0].Current); CurrentNote := 0; if Lines[0].Current > Lines[0].High then - Lines[0].Current := 0; - Lines[0].Line[Lines[0].Current].Note[CurrentNote].Color := 1; + Lines[0].Current := 0; + Lines[0].Line[Lines[0].Current].Note[CurrentNote].Color := 2; Lyric.AddLine(Lines[0].Current); Lyric.Selected := 0; @@ -600,7 +638,7 @@ begin // decrease tone if SDL_ModState = KMOD_LCTRL then - begin + begin TransposeNote(-1); end; @@ -611,18 +649,18 @@ begin // skip to previous sentence if SDL_ModState = 0 then - begin + begin {$IFDEF UseMIDIPort} MidiOut.PutShort($81, Lines[0].Line[Lines[0].Current].Note[MidiLastNote].Tone + 60, 127); PlaySentenceMidi := false; {$endif} - Lines[0].Line[Lines[0].Current].Note[CurrentNote].Color := 0; + Lines[0].Line[Lines[0].Current].Note[CurrentNote].Color := 1; Dec(Lines[0].Current); CurrentNote := 0; if Lines[0].Current = -1 then - Lines[0].Current := Lines[0].High; - Lines[0].Line[Lines[0].Current].Note[CurrentNote].Color := 1; + Lines[0].Current := Lines[0].High; + Lines[0].Line[Lines[0].Current].Note[CurrentNote].Color := 2; Lyric.AddLine(Lines[0].Current); Lyric.Selected := 0; @@ -632,7 +670,7 @@ begin // increase tone if SDL_ModState = KMOD_LCTRL then - begin + begin TransposeNote(1); end; end; @@ -642,9 +680,9 @@ begin end; // if end; -function TScreenEditSub.ParseInputEditText(PressedKey: cardinal; CharCode: WideChar; PressedDown: boolean): boolean; +function TScreenEditSub.ParseInputEditText(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; var - SDL_ModState: Word; + SDL_ModState: word; begin // used when in Text Edit Mode Result := true; @@ -653,7 +691,16 @@ begin + KMOD_LCTRL + KMOD_RCTRL + KMOD_LALT + KMOD_RALT {+ KMOD_CAPS}); if (PressedDown) then - begin // Key Down + begin + // check normal keys + if (IsPrintableChar(CharCode)) then + begin + Lines[0].Line[Lines[0].Current].Note[CurrentNote].Text := + Lines[0].Line[Lines[0].Current].Note[CurrentNote].Text + UCS4ToUTF8String(CharCode); + Exit; + end; + + // check special keys case PressedKey of SDLK_ESCAPE: @@ -665,26 +712,21 @@ begin // Exit Text Edit Mode TextEditMode := false; end; - SDLK_0..SDLK_9, SDLK_A..SDLK_Z, SDLK_SPACE, SDLK_MINUS, SDLK_EXCLAIM, SDLK_COMMA, SDLK_SLASH, SDLK_ASTERISK, SDLK_QUESTION, SDLK_QUOTE, SDLK_QUOTEDBL: - begin - Lines[0].Line[Lines[0].Current].Note[CurrentNote].Text := - Lines[0].Line[Lines[0].Current].Note[CurrentNote].Text + CharCode; - end; SDLK_BACKSPACE: begin - Delete(Lines[0].Line[Lines[0].Current].Note[CurrentNote].Text, - Length(Lines[0].Line[Lines[0].Current].Note[CurrentNote].Text), 1); + UTF8Delete(Lines[0].Line[Lines[0].Current].Note[CurrentNote].Text, + LengthUTF8(Lines[0].Line[Lines[0].Current].Note[CurrentNote].Text), 1); end; SDLK_RIGHT: begin // right if SDL_ModState = 0 then - begin - Lines[0].Line[Lines[0].Current].Note[CurrentNote].Color := 0; + begin + Lines[0].Line[Lines[0].Current].Note[CurrentNote].Color := 1; Inc(CurrentNote); if CurrentNote > Lines[0].Line[Lines[0].Current].HighNote then - CurrentNote := 0; - Lines[0].Line[Lines[0].Current].Note[CurrentNote].Color := 1; + CurrentNote := 0; + Lines[0].Line[Lines[0].Current].Note[CurrentNote].Color := 2; Lyric.Selected := CurrentNote; end; end; @@ -692,12 +734,12 @@ begin begin // left if SDL_ModState = 0 then - begin - Lines[0].Line[Lines[0].Current].Note[CurrentNote].Color := 0; + begin + Lines[0].Line[Lines[0].Current].Note[CurrentNote].Color := 1; Dec(CurrentNote); if CurrentNote = -1 then - CurrentNote := Lines[0].Line[Lines[0].Current].HighNote; - Lines[0].Line[Lines[0].Current].Note[CurrentNote].Color := 1; + CurrentNote := Lines[0].Line[Lines[0].Current].HighNote; + Lines[0].Line[Lines[0].Current].Note[CurrentNote].Color := 2; Lyric.Selected := CurrentNote; end; end; @@ -758,9 +800,11 @@ var S: string; begin // temporary -{ for C := 0 to Lines[0].High do + { + for C := 0 to Lines[0].High do for N := 0 to Lines[0].Line[C].HighNut do - Lines[0].Line[C].Note[N].Text := AnsiLowerCase(Lines[0].Line[C].Note[N].Text);} + Lines[0].Line[C].Note[N].Text := UTF8LowerCase(Lines[0].Line[C].Note[N].Text); + } for C := 0 to Lines[0].High do begin @@ -876,9 +920,8 @@ begin NStart := CurrentNote; Lines[0].Line[CNew].Start := Lines[0].Line[CStart].Note[NStart].Start; Lines[0].Line[CNew].Lyric := ''; - Lines[0].Line[CNew].LyricWidth := 0; Lines[0].Line[CNew].End_ := 0; - Lines[0].Line[CNew].BaseNote := 0; // 0.5.0: we modify it later in this procedure + Lines[0].Line[CNew].BaseNote := 0;//High(integer); // TODO: High (integer) will causes a memory exception later in this procedure. Weird! Lines[0].Line[CNew].HighNote := -1; SetLength(Lines[0].Line[CNew].Note, 0); @@ -905,9 +948,19 @@ begin Lines[0].Line[CStart].Note[NStart-1].Length; SetLength(Lines[0].Line[CStart].Note, Lines[0].Line[CStart].HighNote + 1); + //recalculate BaseNote of the divided Sentence + with Lines[0].Line[CStart] do + begin + BaseNote := High(integer); + + for N := 0 to HighNote do + if Note[N].Tone < BaseNote then + BaseNote := Note[N].Tone; + end; + Lines[0].Current := Lines[0].Current + 1; CurrentNote := 0; - Lines[0].Line[Lines[0].Current].Note[CurrentNote].Color := 1; + Lines[0].Line[Lines[0].Current].Note[CurrentNote].Color := 2; Lyric.AddLine(Lines[0].Current); end; @@ -970,7 +1023,7 @@ begin Inc(Note[CurrentNote+1].Start); Dec(Note[CurrentNote+1].Length); Note[CurrentNote+1].Text := '- '; - Note[CurrentNote+1].Color := 0; + Note[CurrentNote+1].Color := 1; end; end; @@ -982,7 +1035,7 @@ begin C := Lines[0].Current; //Do Not delete Last Note - if (Lines[0].High > 0) OR (Lines[0].Line[C].HighNote > 0) then + if (Lines[0].High > 0) or (Lines[0].Line[C].HighNote > 0) then begin // we copy all notes from the next to the selected one @@ -1000,7 +1053,7 @@ begin if CurrentNote > Lines[0].Line[C].HighNote then Dec(CurrentNote); - Lines[0].Line[C].Note[CurrentNote].Color := 1; + Lines[0].Line[C].Note[CurrentNote].Color := 2; end //Last Note of current Sentence Deleted - > Delete Sentence else @@ -1020,7 +1073,7 @@ begin else Lines[0].Current := 0; - Lines[0].Line[Lines[0].Current].Note[CurrentNote].Color := 1; + Lines[0].Line[Lines[0].Current].Note[CurrentNote].Color := 2; end; end; end; @@ -1076,14 +1129,16 @@ var N: integer; NHigh: integer; begin -{ C := Lines[0].Current; + { + C := Lines[0].Current; for N := Lines[0].Line[C].HighNut downto 1 do begin Lines[0].Line[C].Note[N].Text := Lines[0].Line[C].Note[N-1].Text; end; // for - Lines[0].Line[C].Note[0].Text := '- ';} + Lines[0].Line[C].Note[0].Text := '- '; + } C := Lines[0].Current; NHigh := Lines[0].Line[C].HighNote; @@ -1172,7 +1227,6 @@ begin CopySentence(Src + C, Dst + C); end; - constructor TScreenEditSub.Create; begin inherited Create; @@ -1225,23 +1279,31 @@ begin end; -procedure TScreenEditSub.onShow; +procedure TScreenEditSub.OnShow; +var + FileExt: IPath; begin inherited; - Log.LogStatus('Initializing', 'TEditScreen.onShow'); + Log.LogStatus('Initializing', 'TEditScreen.OnShow'); Lyric := TEditorLyrics.Create; ResetSingTemp; try - //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(); + //Check if File is XML + FileExt := CurrentSong.FileName.GetExtension; + if FileExt.ToUTF8 = '.xml' then + Error := not CurrentSong.LoadXMLSong() + else + begin + // reread header with custom tags + Error := not CurrentSong.Analyse(true); + if not Error then + Error := not CurrentSong.LoadSong; + end; except - Error := True; + Error := true; end; if Error then @@ -1261,19 +1323,19 @@ begin {$ENDIF} Text[TextTitle].Text := CurrentSong.Title; Text[TextArtist].Text := CurrentSong.Artist; - Text[TextMp3].Text := CurrentSong.Mp3; + Text[TextMp3].Text := CurrentSong.Mp3.ToUTF8; Lines[0].Current := 0; CurrentNote := 0; - Lines[0].Line[0].Note[0].Color := 1; - AudioPlayback.Open(CurrentSong.Path + CurrentSong.Mp3); + Lines[0].Line[0].Note[0].Color := 2; + AudioPlayback.Open(CurrentSong.Path.Append(CurrentSong.Mp3)); //Set Down Music Volume for Better hearability of Midi Sounds //Music.SetVolume(0.4); Lyric.Clear; Lyric.X := 400; Lyric.Y := 500; - Lyric.Align := center; + Lyric.Align := atCenter; Lyric.Size := 42; Lyric.ColR := 0; Lyric.ColG := 0; @@ -1306,7 +1368,6 @@ begin {$IFDEF UseMIDIPort} MidiPos := USTime.GetTime - MidiTime + MidiStart; - // stop the music if (MidiPos > MidiStop) then begin @@ -1325,7 +1386,6 @@ begin if (Lines[0].Line[Lines[0].Current].Note[Pet].Start = AktBeat) then begin - LastClick := AktBeat; {$IFDEF UseMIDIPort} if Pet > 0 then @@ -1406,7 +1466,7 @@ begin Result := true; end; -procedure TScreenEditSub.onHide; +procedure TScreenEditSub.OnHide; begin {$IFDEF UseMIDIPort} MidiOut.Close; @@ -1417,7 +1477,8 @@ begin end; function TScreenEditSub.GetNoteName(Note: integer): string; -var N1, N2: integer; +var + N1, N2: integer; begin if (Note > 0) then begin diff --git a/Lua/src/screens/UScreenLevel.pas b/Lua/src/screens/UScreenLevel.pas index a632cf78..1ead9773 100644 --- a/Lua/src/screens/UScreenLevel.pas +++ b/Lua/src/screens/UScreenLevel.pas @@ -34,46 +34,58 @@ interface {$I switches.inc} uses - UMenu, SDL, UDisplay, UMusic, UFiles, SysUtils, UThemes; + UMenu, + SDL, + UDisplay, + UMusic, + UFiles, + SysUtils, + UThemes; type TScreenLevel = class(TMenu) public constructor Create; override; - function ParseInput(PressedKey: Cardinal; CharCode: WideChar; PressedDown: Boolean): Boolean; override; - procedure onShow; override; + function ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; override; + procedure OnShow; override; procedure SetAnimationProgress(Progress: real); override; end; implementation -uses UGraphic, - UMain, - UIni, - USong, - UTexture; - -function TScreenLevel.ParseInput(PressedKey: Cardinal; CharCode: WideChar; PressedDown: Boolean): Boolean; +uses + UGraphic, + UMain, + UIni, + USong, + UTexture, + UUnicodeUtils; + +function TScreenLevel.ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; begin Result := true; - If (PressedDown) Then + if (PressedDown) then begin // Key Down // check normal keys - case WideCharUpperCase(CharCode)[1] of - 'Q': + case UCS4UpperCase(CharCode) of + Ord('Q'): begin Result := false; Exit; end; end; - + // check special keys case PressedKey of SDLK_ESCAPE, SDLK_BACKSPACE : begin AudioPlayback.PlaySound(SoundLib.Back); - FadeTo(@ScreenName); + + if Ini.OnSongClick = sSelectPlayer then + FadeTo(@ScreenMain) + else + FadeTo(@ScreenName); end; SDLK_RETURN: @@ -98,8 +110,6 @@ begin end; constructor TScreenLevel.Create; -//var -// I: integer; // Auto Removed, Unused Variable begin inherited Create; @@ -112,7 +122,7 @@ begin Interaction := 0; end; -procedure TScreenLevel.onShow; +procedure TScreenLevel.OnShow; begin inherited; diff --git a/Lua/src/screens/UScreenLoading.pas b/Lua/src/screens/UScreenLoading.pas index f4a3508a..e368f181 100644 --- a/Lua/src/screens/UScreenLoading.pas +++ b/Lua/src/screens/UScreenLoading.pas @@ -43,18 +43,20 @@ uses type TScreenLoading = class(TMenu) public - Fadeout: boolean; + Fadeout: boolean; + constructor Create; override; - procedure onShow; override; - function ParseInput(PressedKey: Cardinal; CharCode: WideChar; PressedDown: Boolean): Boolean; override; + procedure OnShow; override; + function ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; override; end; implementation -uses UGraphic, - UTime; +uses + UGraphic, + UTime; -function TScreenLoading.ParseInput(PressedKey: Cardinal; CharCode: WideChar; PressedDown: Boolean): Boolean; +function TScreenLoading.ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; begin Result := true; end; @@ -68,7 +70,7 @@ begin Fadeout := false; end; -procedure TScreenLoading.onShow; +procedure TScreenLoading.OnShow; begin inherited; end; diff --git a/Lua/src/screens/UScreenMain.pas b/Lua/src/screens/UScreenMain.pas index c590930c..b342281c 100644 --- a/Lua/src/screens/UScreenMain.pas +++ b/Lua/src/screens/UScreenMain.pas @@ -49,13 +49,10 @@ type TextDescriptionLong: integer; constructor Create; override; - function ParseInput(PressedKey: cardinal; CharCode: widechar; + function ParseInput(PressedKey: Cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; override; - procedure onShow; override; - procedure InteractNext; override; - procedure InteractPrev; override; - procedure InteractInc; override; - procedure InteractDec; override; + procedure OnShow; override; + procedure SetInteraction(Num: integer); override; procedure SetAnimationProgress(Progress: real); override; end; @@ -63,7 +60,7 @@ implementation uses UGraphic, - UMain, + UNote, UIni, UTexture, USongs, @@ -72,14 +69,15 @@ uses UParty, UDLLManager, UScreenCredits, - USkins; + USkins, + UUnicodeUtils; -function TScreenMain.ParseInput(PressedKey: cardinal; CharCode: widechar; +function TScreenMain.ParseInput(PressedKey: Cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; var SDL_ModState: word; begin - Result := True; + Result := true; SDL_ModState := SDL_GetModState and (KMOD_LSHIFT + KMOD_RSHIFT + KMOD_LCTRL + KMOD_RCTRL + KMOD_LALT + KMOD_RALT); @@ -87,22 +85,19 @@ begin if (PressedDown) then begin // Key Down // check normal keys - case WideCharUpperCase(CharCode)[1] of - 'Q': - begin - Result := False; + case UCS4UpperCase(CharCode) of + Ord('Q'): begin + Result := false; Exit; end; - 'C': - begin + Ord('C'): begin if (SDL_ModState = KMOD_LALT) then begin FadeTo(@ScreenCredits, SoundLib.Start); Exit; end; end; - 'M': - begin + Ord('M'): begin if (Ini.Players >= 1) and (Length(DLLMan.Plugins) >= 1) then begin FadeTo(@ScreenPartyOptions, SoundLib.Start); @@ -110,14 +105,12 @@ begin end; end; - 'S': - begin + Ord('S'): begin FadeTo(@ScreenStatMain, SoundLib.Start); Exit; end; - 'E': - begin + Ord('E'): begin FadeTo(@ScreenEdit, SoundLib.Start); Exit; end; @@ -128,7 +121,7 @@ begin SDLK_ESCAPE, SDLK_BACKSPACE: begin - Result := False; + Result := false; end; SDLK_RETURN: @@ -143,8 +136,13 @@ begin if (Ini.Players = 4) then PlayersPlay := 6; - ScreenName.Goto_SingScreen := False; - FadeTo(@ScreenName, SoundLib.Start); + if Ini.OnSongClick = sSelectPlayer then + FadeTo(@ScreenLevel) + else + begin + ScreenName.Goto_SingScreen := false; + FadeTo(@ScreenName, SoundLib.Start); + end; end else //show error message ScreenPopupError.ShowPopup(Language.Translate('ERROR_NO_SONGS')); @@ -170,7 +168,11 @@ begin //Editor if Interaction = 3 then begin + {$IFDEF UseMIDIPort} FadeTo(@ScreenEdit, SoundLib.Start); + {$ELSE} + ScreenPopupError.ShowPopup(Language.Translate('ERROR_NO_EDITOR')); + {$ENDIF} end; //Options @@ -182,7 +184,7 @@ begin //Exit if Interaction = 5 then begin - Result := False; + Result := false; end; end; {** @@ -230,7 +232,7 @@ begin Interaction := 0; end; -procedure TScreenMain.onShow; +procedure TScreenMain.OnShow; begin inherited; @@ -240,36 +242,18 @@ begin *} Party.Clear; + { display cursor (on moved) } + Display.SetCursor; + {** * Start background music *} SoundLib.StartBgMusic; end; -procedure TScreenMain.InteractNext; -begin - inherited InteractNext; - Text[TextDescription].Text := Theme.Main.Description[Interaction]; - Text[TextDescriptionLong].Text := Theme.Main.DescriptionLong[Interaction]; -end; - -procedure TScreenMain.InteractPrev; -begin - inherited InteractPrev; - Text[TextDescription].Text := Theme.Main.Description[Interaction]; - Text[TextDescriptionLong].Text := Theme.Main.DescriptionLong[Interaction]; -end; - -procedure TScreenMain.InteractDec; -begin - inherited InteractDec; - Text[TextDescription].Text := Theme.Main.Description[Interaction]; - Text[TextDescriptionLong].Text := Theme.Main.DescriptionLong[Interaction]; -end; - -procedure TScreenMain.InteractInc; +procedure TScreenMain.SetInteraction(Num: integer); begin - inherited InteractInc; + inherited SetInteraction(Num); Text[TextDescription].Text := Theme.Main.Description[Interaction]; Text[TextDescriptionLong].Text := Theme.Main.DescriptionLong[Interaction]; end; diff --git a/Lua/src/screens/UScreenName.pas b/Lua/src/screens/UScreenName.pas index dd11b882..42af50d7 100644 --- a/Lua/src/screens/UScreenName.pas +++ b/Lua/src/screens/UScreenName.pas @@ -34,40 +34,52 @@ interface {$I switches.inc} uses - UMenu, SDL, UDisplay, UMusic, UFiles, SysUtils, UThemes; + SysUtils, + SDL, + UDisplay, + UFiles, + UMenu, + UMusic, + UThemes; type TScreenName = class(TMenu) public - Goto_SingScreen: Boolean; //If True then next Screen in SingScreen + Goto_SingScreen: boolean; //If true then next Screen in SingScreen constructor Create; override; - function ParseInput(PressedKey: Cardinal; CharCode: WideChar; PressedDown: Boolean): Boolean; override; - procedure onShow; override; + function ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; override; + procedure OnShow; override; procedure SetAnimationProgress(Progress: real); override; end; implementation -uses UGraphic, UMain, UIni, UTexture, UCommon; +uses + UCommon, + UGraphic, + UIni, + UNote, + UTexture, + UUnicodeUtils; -function TScreenName.ParseInput(PressedKey: Cardinal; CharCode: WideChar; PressedDown: Boolean): Boolean; +function TScreenName.ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; var - I: integer; -SDL_ModState: Word; + I: integer; + SDL_ModState: word; begin Result := true; - If (PressedDown) Then + if (PressedDown) then begin // Key Down SDL_ModState := SDL_GetModState and (KMOD_LSHIFT + KMOD_RSHIFT + KMOD_LCTRL + KMOD_RCTRL + KMOD_LALT + KMOD_RALT); // check normal keys - if (IsAlphaNumericChar(CharCode) or - {(CharCode in [' ','-','_','!',',','<','/','*','?','''','"']))} IsPunctuationChar(CharCode)) then + if (IsPrintableChar(CharCode)) then begin - Button[Interaction].Text[0].Text := Button[Interaction].Text[0].Text + CharCode; + Button[Interaction].Text[0].Text := Button[Interaction].Text[0].Text + + UCS4ToUTF8String(CharCode); Exit; end; @@ -183,10 +195,9 @@ begin Button[Interaction].Text[0].Text := Ini.NameTemplate[11]; end; - SDLK_BACKSPACE: begin - Button[Interaction].Text[0].DeleteLastL; + Button[Interaction].Text[0].DeleteLastLetter(); end; SDLK_ESCAPE : @@ -211,7 +222,7 @@ begin else FadeTo(@ScreenLevel); - GoTo_SingScreen := False; + GoTo_SingScreen := false; end; // Up and Down could be done at the same time, @@ -233,14 +244,13 @@ begin LoadFromTheme(Theme.Name); - for I := 1 to 6 do AddButton(Theme.Name.ButtonPlayer[I]); Interaction := 0; end; -procedure TScreenName.onShow; +procedure TScreenName.OnShow; var I: integer; begin @@ -249,12 +259,14 @@ begin for I := 1 to 6 do Button[I-1].Text[0].Text := Ini.Name[I-1]; - for I := 1 to PlayersPlay do begin + for I := 1 to PlayersPlay do + begin Button[I-1].Visible := true; Button[I-1].Selectable := true; end; - for I := PlayersPlay+1 to 6 do begin + for I := PlayersPlay+1 to 6 do + begin Button[I-1].Visible := false; Button[I-1].Selectable := false; end; diff --git a/Lua/src/screens/UScreenOpen.pas b/Lua/src/screens/UScreenOpen.pas index c4d773e7..70b883c4 100644 --- a/Lua/src/screens/UScreenOpen.pas +++ b/Lua/src/screens/UScreenOpen.pas @@ -34,10 +34,13 @@ interface {$I switches.inc} uses + Math, + SysUtils, + gl, + SDL, + UPath, UMenu, UMusic, - SDL, - SysUtils, UFiles, UTime, USongs, @@ -46,26 +49,31 @@ uses UTexture, UMenuText, ULyrics, - Math, - gl, UThemes; type TScreenOpen = class(TMenu) private - TextF: array[0..1] of integer; - TextN: integer; - public - Tex_Background: TTexture; - FadeOut: boolean; - Path: string; - BackScreen: pointer; + //fTextF: array[0..1] of integer; + fTextN: integer; // text-box ID of filename + fFilename: IPath; + fBackScreen: PMenu; + procedure AddBox(X, Y, W, H: real); + public constructor Create; override; - procedure onShow; override; - function ParseInput(PressedKey: cardinal; CharCode: WideChar; PressedDown: boolean): boolean; override; -// function Draw: boolean; override; -// procedure Finish; + procedure OnShow; override; + function ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; override; + + {** + * Set by the caller to provide a default filename. + * Set to the selected filename after calling this screen or to PATH_NONE + * if the screen was aborted. + * TODO: maybe pass this value with a callback OnValueChanged() + *} + property Filename: IPath READ fFilename WRITE fFilename; + {** The screen that is shown after this screen is closed (set by the caller) *} + property BackScreen: PMenu READ fBackScreen WRITE fBackScreen; end; implementation @@ -74,62 +82,59 @@ uses UGraphic, UDraw, UMain, - USkins; + UScreenEditConvert, + USkins, + UUnicodeUtils; -function TScreenOpen.ParseInput(PressedKey: cardinal; CharCode: WideChar; PressedDown: boolean): boolean; +function TScreenOpen.ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; begin Result := true; if (PressedDown) then // Key Down begin // check normal keys - case CharCode of - '0'..'9', 'a'..'z', 'A'..'Z', ' ', '-', '.', ':', '\': - begin - if Interaction = 0 then - begin - Text[TextN].Text := Text[TextN].Text + CharCode; - end; - end; + if (IsPrintableChar(CharCode)) then + begin + if (Interaction = 0) then + begin + Text[fTextN].Text := Text[fTextN].Text + UCS4ToUTF8String(CharCode); + Exit; + end; end; // check special keys case PressedKey of - SDLK_Q: - begin - Result := false; - end; - 8: // del + SDLK_BACKSPACE: // del begin if Interaction = 0 then begin - Text[TextN].DeleteLastL; + Text[fTextN].DeleteLastLetter; end; end; SDLK_ESCAPE: begin //Empty Filename and go to last Screen - ConversionFileName := ''; + fFileName := PATH_NONE; AudioPlayback.PlaySound(SoundLib.Back); - FadeTo(BackScreen); + FadeTo(fBackScreen); end; SDLK_RETURN: begin if (Interaction = 2) then - begin + begin //Update Filename and go to last Screen - ConversionFileName := Text[TextN].Text; + fFileName := Path(Text[fTextN].Text); AudioPlayback.PlaySound(SoundLib.Back); - FadeTo(BackScreen); + FadeTo(fBackScreen); end else if (Interaction = 1) then begin //Empty Filename and go to last Screen - ConversionFileName := ''; + fFileName := PATH_NONE; AudioPlayback.PlaySound(SoundLib.Back); - FadeTo(BackScreen); + FadeTo(fBackScreen); end; end; @@ -164,21 +169,25 @@ constructor TScreenOpen.Create; begin inherited Create; + fFilename := PATH_NONE; + // line -{ AddStatic(20, 10, 80, 30, 0, 0, 0, 'MainBar', 'JPG', TEXTURE_TYPE_COLORIZED); + { + AddStatic(20, 10, 80, 30, 0, 0, 0, 'MainBar', 'JPG', TEXTURE_TYPE_COLORIZED); AddText(35, 17, 1, 18, 1, 1, 1, 'line'); - TextSentence := AddText(120, 14, 1, 24, 0, 0, 0, '0 / 0');} + TextSentence := AddText(120, 14, 1, 24, 0, 0, 0, '0 / 0'); + } // file list -// AddBox(400, 100, 350, 450); + //AddBox(400, 100, 350, 450); -// TextF[0] := AddText(430, 155, 0, 24, 0, 0, 0, 'a'); -// TextF[1] := AddText(430, 180, 0, 24, 0, 0, 0, 'a'); + //TextF[0] := AddText(430, 155, 0, 24, 0, 0, 0, 'a'); + //TextF[1] := AddText(430, 180, 0, 24, 0, 0, 0, 'a'); // file name AddBox(20, 540, 500, 40); - TextN := AddText(50, 548, 0, 24, 0, 0, 0, ConversionFileName); - AddInteraction(iText, TextN); + fTextN := AddText(50, 548, 0, 24, 0, 0, 0, fFileName.ToUTF8); + AddInteraction(iText, fTextN); // buttons {AddButton(540, 540, 100, 40, Skin.SkinPath + Skin.ButtonF); @@ -195,14 +204,16 @@ begin end; -procedure TScreenOpen.onShow; +procedure TScreenOpen.OnShow; begin inherited; Interaction := 0; + Text[fTextN].Text := fFilename.ToUTF8(); end; -(*function TScreenEditSub.Draw: boolean; +(* +function TScreenEditSub.Draw: boolean; var Min: integer; Sec: integer; @@ -214,6 +225,7 @@ end; procedure TScreenEditSub.Finish; begin // -end;*) +end; +*) end. diff --git a/Lua/src/screens/UScreenOptions.pas b/Lua/src/screens/UScreenOptions.pas index 10785b86..3a046400 100644 --- a/Lua/src/screens/UScreenOptions.pas +++ b/Lua/src/screens/UScreenOptions.pas @@ -34,15 +34,22 @@ interface {$I switches.inc} uses - UMenu, SDL, SysUtils, UDisplay, UMusic, UFiles, UIni, UThemes; + SDL, + SysUtils, + UMenu, + UDisplay, + UMusic, + UFiles, + UIni, + UThemes; type TScreenOptions = class(TMenu) public TextDescription: integer; constructor Create; override; - function ParseInput(PressedKey: Cardinal; CharCode: WideChar; PressedDown: Boolean): Boolean; override; - procedure onShow; override; + function ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; override; + procedure OnShow; override; procedure InteractNext; override; procedure InteractPrev; override; procedure InteractNextRow; override; @@ -52,16 +59,18 @@ type implementation -uses UGraphic; +uses + UGraphic, + UUnicodeUtils; -function TScreenOptions.ParseInput(PressedKey: Cardinal; CharCode: WideChar; PressedDown: Boolean): Boolean; +function TScreenOptions.ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; begin Result := true; - If (PressedDown) Then + if (PressedDown) then begin // Key Down // check normal keys - case WideCharUpperCase(CharCode)[1] of - 'Q': + case UCS4UpperCase(CharCode) of + Ord('Q'): begin Result := false; Exit; @@ -181,7 +190,7 @@ begin Interaction := 0; end; -procedure TScreenOptions.onShow; +procedure TScreenOptions.OnShow; begin inherited; end; diff --git a/Lua/src/screens/UScreenOptionsAdvanced.pas b/Lua/src/screens/UScreenOptionsAdvanced.pas index 8960640d..7116ad40 100644 --- a/Lua/src/screens/UScreenOptionsAdvanced.pas +++ b/Lua/src/screens/UScreenOptionsAdvanced.pas @@ -34,28 +34,37 @@ interface {$I switches.inc} uses - UMenu, SDL, UDisplay, UMusic, UFiles, UIni, UThemes; + SDL, + UMenu, + UDisplay, + UMusic, + UFiles, + UIni, + UThemes; type TScreenOptionsAdvanced = class(TMenu) public constructor Create; override; - function ParseInput(PressedKey: Cardinal; CharCode: WideChar; PressedDown: Boolean): Boolean; override; - procedure onShow; override; + function ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; override; + procedure OnShow; override; end; implementation -uses UGraphic, SysUtils; +uses + UGraphic, + UUnicodeUtils, + SysUtils; -function TScreenOptionsAdvanced.ParseInput(PressedKey: Cardinal; CharCode: WideChar; PressedDown: Boolean): Boolean; +function TScreenOptionsAdvanced.ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; begin Result := true; - If (PressedDown) Then + if (PressedDown) then begin // Key Down // check normal keys - case WideCharUpperCase(CharCode)[1] of - 'Q': + case UCS4UpperCase(CharCode) of + Ord('Q'): begin Result := false; Exit; @@ -76,7 +85,8 @@ begin begin //SelectLoadAnimation Hidden because it is useless atm //if SelInteraction = 7 then begin - if SelInteraction = 6 then begin + if SelInteraction = 6 then + begin Ini.Save; AudioPlayback.PlaySound(SoundLib.Back); FadeTo(@ScreenOptions); @@ -90,7 +100,8 @@ begin begin //SelectLoadAnimation Hidden because it is useless atm //if (SelInteraction >= 0) and (SelInteraction <= 6) then begin - if (SelInteraction >= 0) and (SelInteraction <= 5) then begin + if (SelInteraction >= 0) and (SelInteraction <= 5) then + begin AudioPlayback.PlaySound(SoundLib.Option); InteractInc; end; @@ -99,7 +110,8 @@ begin begin //SelectLoadAnimation Hidden because it is useless atm //if (SelInteraction >= 0) and (SelInteraction <= 6) then begin - if (SelInteraction >= 0) and (SelInteraction <= 5) then begin + if (SelInteraction >= 0) and (SelInteraction <= 5) then + begin AudioPlayback.PlaySound(SoundLib.Option); InteractDec; end; @@ -117,13 +129,30 @@ begin LoadFromTheme(Theme.OptionsAdvanced); //SelectLoadAnimation Hidden because it is useless atm - //AddSelect(Theme.OptionsAdvanced.SelectLoadAnimation, Ini.LoadAnimation, ILoadAnimation); - AddSelectSlide(Theme.OptionsAdvanced.SelectScreenFade, Ini.ScreenFade, IScreenFade); - AddSelectSlide(Theme.OptionsAdvanced.SelectEffectSing, Ini.EffectSing, IEffectSing); - AddSelectSlide(Theme.OptionsAdvanced.SelectLineBonus, Ini.LineBonus, ILineBonus); - AddSelectSlide(Theme.OptionsAdvanced.SelectOnSongClick, Ini.OnSongClick, IOnSongClick); - AddSelectSlide(Theme.OptionsAdvanced.SelectAskbeforeDel, Ini.AskBeforeDel, IAskbeforeDel); - AddSelectSlide(Theme.OptionsAdvanced.SelectPartyPopup, Ini.PartyPopup, IPartyPopup); + //AddSelect(Theme.OptionsAdvanced.SelectLoadAnimation, Ini.LoadAnimation, ILoadAnimationTranslated); + Theme.OptionsAdvanced.SelectScreenFade.showArrows := true; + Theme.OptionsAdvanced.SelectScreenFade.oneItemOnly := true; + AddSelectSlide(Theme.OptionsAdvanced.SelectScreenFade, Ini.ScreenFade, IScreenFadeTranslated); + + Theme.OptionsAdvanced.SelectEffectSing.showArrows := true; + Theme.OptionsAdvanced.SelectEffectSing.oneItemOnly := true; + AddSelectSlide(Theme.OptionsAdvanced.SelectEffectSing, Ini.EffectSing, IEffectSingTranslated); + + Theme.OptionsAdvanced.SelectLineBonus.showArrows := true; + Theme.OptionsAdvanced.SelectLineBonus.oneItemOnly := true; + AddSelectSlide(Theme.OptionsAdvanced.SelectLineBonus, Ini.LineBonus, ILineBonusTranslated); + + Theme.OptionsAdvanced.SelectOnSongClick.showArrows := true; + Theme.OptionsAdvanced.SelectOnSongClick.oneItemOnly := true; + AddSelectSlide(Theme.OptionsAdvanced.SelectOnSongClick, Ini.OnSongClick, IOnSongClickTranslated); + + Theme.OptionsAdvanced.SelectAskbeforeDel.showArrows := true; + Theme.OptionsAdvanced.SelectAskbeforeDel.oneItemOnly := true; + AddSelectSlide(Theme.OptionsAdvanced.SelectAskbeforeDel, Ini.AskBeforeDel, IAskbeforeDelTranslated); + + Theme.OptionsAdvanced.SelectPartyPopup.showArrows := true; + Theme.OptionsAdvanced.SelectPartyPopup.oneItemOnly := true; + AddSelectSlide(Theme.OptionsAdvanced.SelectPartyPopup, Ini.PartyPopup, IPartyPopupTranslated); AddButton(Theme.OptionsAdvanced.ButtonExit); if (Length(Button[0].Text)=0) then @@ -132,7 +161,7 @@ begin Interaction := 0; end; -procedure TScreenOptionsAdvanced.onShow; +procedure TScreenOptionsAdvanced.OnShow; begin inherited; diff --git a/Lua/src/screens/UScreenOptionsGame.pas b/Lua/src/screens/UScreenOptionsGame.pas index e634419b..caeaad6e 100644 --- a/Lua/src/screens/UScreenOptionsGame.pas +++ b/Lua/src/screens/UScreenOptionsGame.pas @@ -34,30 +34,40 @@ interface {$I switches.inc} uses - UMenu, SDL, UDisplay, UMusic, UFiles, UIni, UThemes, USongs; + SDL, + UMenu, + UDisplay, + UMusic, + UFiles, + UIni, + UThemes, + USongs; type TScreenOptionsGame = class(TMenu) public old_Tabs, old_Sorting: integer; constructor Create; override; - function ParseInput(PressedKey: Cardinal; CharCode: WideChar; PressedDown: Boolean): Boolean; override; - procedure onShow; override; + function ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; override; + procedure OnShow; override; procedure RefreshSongs; end; implementation -uses UGraphic, SysUtils; +uses + UGraphic, + UUnicodeUtils, + SysUtils; -function TScreenOptionsGame.ParseInput(PressedKey: Cardinal; CharCode: WideChar; PressedDown: Boolean): Boolean; +function TScreenOptionsGame.ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; begin Result := true; - If (PressedDown) Then + if PressedDown then begin // Key Down // check normal keys - case WideCharUpperCase(CharCode)[1] of - 'Q': + case UCS4UpperCase(CharCode) of + Ord('Q'): begin Result := false; Exit; @@ -71,12 +81,12 @@ begin begin AudioPlayback.PlaySound(SoundLib.Back); RefreshSongs; - FadeTo(@ScreenOptions); end; SDLK_RETURN: begin - if SelInteraction = 6 then begin + if SelInteraction = 6 then + begin AudioPlayback.PlaySound(SoundLib.Back); RefreshSongs; FadeTo(@ScreenOptions); @@ -116,15 +126,34 @@ begin old_Sorting := Ini.Sorting; old_Tabs := Ini.Tabs; + Theme.OptionsGame.SelectPlayers.showArrows := true; + Theme.OptionsGame.SelectPlayers.oneItemOnly := true; AddSelectSlide(Theme.OptionsGame.SelectPlayers, Ini.Players, IPlayers); - AddSelectSlide(Theme.OptionsGame.SelectDifficulty, Ini.Difficulty, IDifficulty); - AddSelectSlide(Theme.OptionsGame.SelectLanguage, Ini.Language, ILanguage); - AddSelectSlide(Theme.OptionsGame.SelectTabs, Ini.Tabs, ITabs); - AddSelectSlide(Theme.OptionsGame.SelectSorting, Ini.Sorting, ISorting); - AddSelectSlide(Theme.OptionsGame.SelectDebug, Ini.Debug, IDebug); + + Theme.OptionsGame.SelectDifficulty.showArrows := true; + Theme.OptionsGame.SelectDifficulty.oneItemOnly := true; + AddSelectSlide(Theme.OptionsGame.SelectDifficulty, Ini.Difficulty, IDifficultyTranslated); + + Theme.OptionsGame.SelectLanguage.showArrows := true; + Theme.OptionsGame.SelectLanguage.oneItemOnly := true; + AddSelectSlide(Theme.OptionsGame.SelectLanguage, Ini.Language, ILanguageTranslated); + + Theme.OptionsGame.SelectTabs.showArrows := true; + Theme.OptionsGame.SelectTabs.oneItemOnly := true; + AddSelectSlide(Theme.OptionsGame.SelectTabs, Ini.Tabs, ITabsTranslated); + + Theme.OptionsGame.SelectSorting.showArrows := true; + Theme.OptionsGame.SelectSorting.oneItemOnly := true; + AddSelectSlide(Theme.OptionsGame.SelectSorting, Ini.Sorting, ISortingTranslated); + + Theme.OptionsGame.SelectDebug.showArrows := true; + Theme.OptionsGame.SelectDebug.oneItemOnly := true; + AddSelectSlide(Theme.OptionsGame.SelectDebug, Ini.Debug, IDebugTranslated); + + AddButton(Theme.OptionsGame.ButtonExit); - if (Length(Button[0].Text)=0) then + if (Length(Button[0].Text) = 0) then AddButtonText(14, 20, Theme.Options.Description[7]); end; @@ -132,11 +161,11 @@ end; //Refresh Songs Patch procedure TScreenOptionsGame.RefreshSongs; begin -if (ini.Sorting <> old_Sorting) or (ini.Tabs <> old_Tabs) then + if (ini.Sorting <> old_Sorting) or (ini.Tabs <> old_Tabs) then ScreenSong.Refresh; end; -procedure TScreenOptionsGame.onShow; +procedure TScreenOptionsGame.OnShow; begin inherited; diff --git a/Lua/src/screens/UScreenOptionsGraphics.pas b/Lua/src/screens/UScreenOptionsGraphics.pas index 2e2b4f0e..8ca13f09 100644 --- a/Lua/src/screens/UScreenOptionsGraphics.pas +++ b/Lua/src/screens/UScreenOptionsGraphics.pas @@ -46,22 +46,26 @@ type TScreenOptionsGraphics = class(TMenu) public constructor Create; override; - function ParseInput(PressedKey: Cardinal; CharCode: WideChar; PressedDown: Boolean): Boolean; override; - procedure onShow; override; + function ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; override; + procedure OnShow; override; end; implementation -uses UGraphic, UMain, SysUtils, TypInfo; +uses + UGraphic, + UMain, + UUnicodeUtils, + SysUtils; -function TScreenOptionsGraphics.ParseInput(PressedKey: Cardinal; CharCode: WideChar; PressedDown: Boolean): Boolean; +function TScreenOptionsGraphics.ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; begin Result := true; - If (PressedDown) Then + if (PressedDown) then begin // Key Down // check normal keys - case WideCharUpperCase(CharCode)[1] of - 'Q': + case UCS4UpperCase(CharCode) of + Ord('Q'): begin Result := false; Exit; @@ -80,10 +84,12 @@ begin end; SDLK_RETURN: begin -{ if SelInteraction <= 1 then begin +{ if SelInteraction <= 1 then + begin Restart := true; end;} - if SelInteraction = 6 then begin + if SelInteraction = 6 then + begin Ini.Save; AudioPlayback.PlaySound(SoundLib.Back); // FIXME: changing the video mode does not work this way in windows @@ -101,14 +107,16 @@ begin InteractPrev; SDLK_RIGHT: begin - if (SelInteraction >= 0) and (SelInteraction < 6) then begin + if (SelInteraction >= 0) and (SelInteraction < 6) then + begin AudioPlayback.PlaySound(SoundLib.Option); InteractInc; end; end; SDLK_LEFT: begin - if (SelInteraction >= 0) and (SelInteraction < 6) then begin + if (SelInteraction >= 0) and (SelInteraction < 6) then + begin AudioPlayback.PlaySound(SoundLib.Option); InteractDec; end; @@ -124,13 +132,29 @@ begin inherited Create; LoadFromTheme(Theme.OptionsGraphics); - AddSelectSlide(Theme.OptionsGraphics.SelectResolution, Ini.Resolution, IResolution); - AddSelectSlide(Theme.OptionsGraphics.SelectFullscreen, Ini.Fullscreen, IFullscreen); - AddSelectSlide(Theme.OptionsGraphics.SelectDepth, Ini.Depth, IDepth); - AddSelectSlide(Theme.OptionsGraphics.SelectVisualizer, Ini.VisualizerOption, IVisualizer); - AddSelectSlide(Theme.OptionsGraphics.SelectOscilloscope, Ini.Oscilloscope, IOscilloscope); - AddSelectSlide(Theme.OptionsGraphics.SelectMovieSize, Ini.MovieSize, IMovieSize); + Theme.OptionsGraphics.SelectResolution.showArrows := true; + Theme.OptionsGraphics.SelectResolution.oneItemOnly := true; + AddSelectSlide(Theme.OptionsGraphics.SelectResolution, Ini.Resolution, IResolution); + + Theme.OptionsGraphics.SelectFullscreen.showArrows := true; + Theme.OptionsGraphics.SelectFullscreen.oneItemOnly := true; + AddSelectSlide(Theme.OptionsGraphics.SelectFullscreen, Ini.Fullscreen, IFullScreenTranslated); + + Theme.OptionsGraphics.SelectDepth.showArrows := true; + Theme.OptionsGraphics.SelectDepth.oneItemOnly := true; + AddSelectSlide(Theme.OptionsGraphics.SelectDepth, Ini.Depth, IDepth); + + Theme.OptionsGraphics.SelectVisualizer.showArrows := true; + Theme.OptionsGraphics.SelectVisualizer.oneItemOnly := true; + AddSelectSlide(Theme.OptionsGraphics.SelectVisualizer, Ini.VisualizerOption, IVisualizerTranslated); + + Theme.OptionsGraphics.SelectOscilloscope.showArrows := true; + Theme.OptionsGraphics.SelectOscilloscope.oneItemOnly := true; + AddSelectSlide(Theme.OptionsGraphics.SelectOscilloscope, Ini.Oscilloscope, IOscilloscopeTranslated); + Theme.OptionsGraphics.SelectMovieSize.showArrows := true; + Theme.OptionsGraphics.SelectMovieSize.oneItemOnly := true; + AddSelectSlide(Theme.OptionsGraphics.SelectMovieSize, Ini.MovieSize, IMovieSizeTranslated); AddButton(Theme.OptionsGraphics.ButtonExit); if (Length(Button[0].Text)=0) then @@ -138,7 +162,7 @@ begin end; -procedure TScreenOptionsGraphics.onShow; +procedure TScreenOptionsGraphics.OnShow; begin inherited; diff --git a/Lua/src/screens/UScreenOptionsLyrics.pas b/Lua/src/screens/UScreenOptionsLyrics.pas index b5228a52..0ef4e2a6 100644 --- a/Lua/src/screens/UScreenOptionsLyrics.pas +++ b/Lua/src/screens/UScreenOptionsLyrics.pas @@ -46,22 +46,25 @@ type TScreenOptionsLyrics = class(TMenu) public constructor Create; override; - function ParseInput(PressedKey: Cardinal; CharCode: WideChar; PressedDown: Boolean): Boolean; override; - procedure onShow; override; + function ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; override; + procedure OnShow; override; end; implementation -uses UGraphic, SysUtils; +uses + UGraphic, + UUnicodeUtils, + SysUtils; -function TScreenOptionsLyrics.ParseInput(PressedKey: Cardinal; CharCode: WideChar; PressedDown: Boolean): Boolean; +function TScreenOptionsLyrics.ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; begin Result := true; - If (PressedDown) Then + if (PressedDown) then begin // Key Down // check normal keys - case WideCharUpperCase(CharCode)[1] of - 'Q': + case UCS4UpperCase(CharCode) of + Ord('Q'): begin Result := false; Exit; @@ -80,7 +83,8 @@ begin end; SDLK_RETURN: begin - if SelInteraction = 3 then begin + if SelInteraction = 3 then + begin Ini.Save; AudioPlayback.PlaySound(SoundLib.Back); FadeTo(@ScreenOptions); @@ -92,14 +96,16 @@ begin InteractPrev; SDLK_RIGHT: begin - if (SelInteraction >= 0) and (SelInteraction <= 3) then begin + if (SelInteraction >= 0) and (SelInteraction <= 3) then + begin AudioPlayback.PlaySound(SoundLib.Option); InteractInc; end; end; SDLK_LEFT: begin - if (SelInteraction >= 0) and (SelInteraction <= 3) then begin + if (SelInteraction >= 0) and (SelInteraction <= 3) then + begin AudioPlayback.PlaySound(SoundLib.Option); InteractDec; end; @@ -114,11 +120,17 @@ begin LoadFromTheme(Theme.OptionsLyrics); - AddSelectSlide(Theme.OptionsLyrics.SelectLyricsFont, Ini.LyricsFont, ILyricsFont); - AddSelectSlide(Theme.OptionsLyrics.SelectLyricsEffect, Ini.LyricsEffect, ILyricsEffect); - //AddSelect(Theme.OptionsLyrics.SelectSolmization, Ini.Solmization, ISolmization); GAH!!!!11 DIE!!! - AddSelectSlide(Theme.OptionsLyrics.SelectNoteLines, Ini.NoteLines, INoteLines); + Theme.OptionsLyrics.SelectLyricsFont.showArrows := true; + Theme.OptionsLyrics.SelectLyricsFont.oneItemOnly := true; + AddSelectSlide(Theme.OptionsLyrics.SelectLyricsFont, Ini.LyricsFont, ILyricsFontTranslated); + + Theme.OptionsLyrics.SelectLyricsEffect.showArrows := true; + Theme.OptionsLyrics.SelectLyricsEffect.oneItemOnly := true; + AddSelectSlide(Theme.OptionsLyrics.SelectLyricsEffect, Ini.LyricsEffect, ILyricsEffectTranslated); + Theme.OptionsLyrics.SelectNoteLines.showArrows := true; + Theme.OptionsLyrics.SelectNoteLines.oneItemOnly := true; + AddSelectSlide(Theme.OptionsLyrics.SelectNoteLines, Ini.NoteLines, INoteLinesTranslated); AddButton(Theme.OptionsLyrics.ButtonExit); if (Length(Button[0].Text)=0) then @@ -126,7 +138,7 @@ begin end; -procedure TScreenOptionsLyrics.onShow; +procedure TScreenOptionsLyrics.OnShow; begin inherited; diff --git a/Lua/src/screens/UScreenOptionsRecord.pas b/Lua/src/screens/UScreenOptionsRecord.pas index b622f56c..828c20f6 100644 --- a/Lua/src/screens/UScreenOptionsRecord.pas +++ b/Lua/src/screens/UScreenOptionsRecord.pas @@ -61,8 +61,8 @@ type PreviewDeviceIndex: integer; // string arrays for select-slide options - InputSourceNames: array of string; - InputDeviceNames: array of string; + InputSourceNames: array of UTF8String; + InputDeviceNames: array of UTF8String; // dynamic generated themes for channel select-sliders SelectSlideChannelTheme: array of TThemeSelectSlide; @@ -95,9 +95,9 @@ type public constructor Create; override; function Draw: boolean; override; - function ParseInput(PressedKey: Cardinal; CharCode: WideChar; PressedDown: Boolean): Boolean; override; - procedure onShow; override; - procedure onHide; override; + function ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; override; + procedure OnShow; override; + procedure OnHide; override; end; const @@ -126,33 +126,34 @@ uses UFiles, UDisplay, UIni, + UUnicodeUtils, ULog; -function TScreenOptionsRecord.ParseInput(PressedKey: Cardinal; CharCode: WideChar; PressedDown: Boolean): Boolean; +function TScreenOptionsRecord.ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; begin Result := true; - If (PressedDown) Then + if (PressedDown) then begin // Key Down // check normal keys - case WideCharUpperCase(CharCode)[1] of - 'Q': + case UCS4UpperCase(CharCode) of + Ord('Q'): begin Result := false; Exit; end; - '+': + Ord('+'): begin // FIXME: add a nice volume-slider instead // or at least provide visualization and acceleration if the user holds the key pressed. ChangeVolume(0.02); end; - '-': + Ord('-'): begin // FIXME: add a nice volume-slider instead // or at least provide visualization and acceleration if the user holds the key pressed. ChangeVolume(-0.02); end; - 'T': + Ord('T'): begin if ((SDL_GetModState() and KMOD_SHIFT) <> 0) then Ini.ThresholdIndex := (Ini.ThresholdIndex + Length(IThresholdVals) - 1) mod Length(IThresholdVals) @@ -166,8 +167,8 @@ begin SDLK_ESCAPE, SDLK_BACKSPACE: begin - // Escape -> save nothing - just leave this screen - + // TODO: Show Save/Abort screen + Ini.Save; AudioPlayback.PlaySound(SoundLib.Back); FadeTo(@ScreenOptions); end; @@ -244,6 +245,8 @@ begin InputDeviceNames[DeviceIndex] := AudioInputProcessor.DeviceList[DeviceIndex].Name; end; // add device-selection slider (InteractionID: 0) + Theme.OptionsRecord.SelectSlideCard.showArrows := true; + Theme.OptionsRecord.SelectSlideCard.oneItemOnly := true; AddSelectSlide(Theme.OptionsRecord.SelectSlideCard, CurrentDeviceIndex, InputDeviceNames); // init source-selection slider @@ -252,6 +255,9 @@ begin begin InputSourceNames[SourceIndex] := InputDevice.Source[SourceIndex].Name; end; + + Theme.OptionsRecord.SelectSlideInput.showArrows := true; + Theme.OptionsRecord.SelectSlideInput.oneItemOnly := true; // add source-selection slider (InteractionID: 1) SelectInputSourceID := AddSelectSlide(Theme.OptionsRecord.SelectSlideInput, InputDeviceCfg.Input, InputSourceNames); @@ -294,7 +300,7 @@ begin // add slider SelectSlideChannelID[ChannelIndex] := AddSelectSlide(ChannelTheme^, - InputDeviceCfg.ChannelToPlayerMap[ChannelIndex], IChannelPlayer); + InputDeviceCfg.ChannelToPlayerMap[ChannelIndex], IChannelPlayerTranslated); end else begin @@ -302,7 +308,7 @@ begin // add slider but hide it and assign a dummy variable to it SelectSlideChannelID[ChannelIndex] := AddSelectSlide(ChannelTheme^, - ChannelToPlayerMapDummy, IChannelPlayer); + ChannelToPlayerMapDummy, IChannelPlayerTranslated); SelectsS[SelectSlideChannelID[ChannelIndex]].Visible := false; end; end; @@ -368,7 +374,7 @@ begin // show slider UpdateSelectSlideOptions(SelectSlideChannelTheme[ChannelIndex], - SelectSlideChannelID[ChannelIndex], IChannelPlayer, + SelectSlideChannelID[ChannelIndex], IChannelPlayerTranslated, InputDeviceCfg.ChannelToPlayerMap[ChannelIndex]); SelectsS[SelectSlideChannelID[ChannelIndex]].Visible := true; end @@ -378,7 +384,7 @@ begin // hide slider and assign a dummy variable to it UpdateSelectSlideOptions(SelectSlideChannelTheme[ChannelIndex], - SelectSlideChannelID[ChannelIndex], IChannelPlayer, + SelectSlideChannelID[ChannelIndex], IChannelPlayerTranslated, ChannelToPlayerMapDummy); SelectsS[SelectSlideChannelID[ChannelIndex]].Visible := false; end; @@ -413,7 +419,7 @@ begin NextVolumePollTime := 0; end; -procedure TScreenOptionsRecord.onShow; +procedure TScreenOptionsRecord.OnShow; var ChannelIndex: integer; begin @@ -431,7 +437,7 @@ begin StartPreview(); end; -procedure TScreenOptionsRecord.onHide; +procedure TScreenOptionsRecord.OnHide; var ChannelIndex: integer; begin @@ -484,7 +490,6 @@ begin PreviewDeviceIndex := -1; end; - procedure TScreenOptionsRecord.DrawVolume(x, y, Width, Height: single); var x1, y1, x2, y2: single; @@ -802,8 +807,7 @@ begin end; end; - Result := True; + Result := true; end; - end. diff --git a/Lua/src/screens/UScreenOptionsSound.pas b/Lua/src/screens/UScreenOptionsSound.pas index 80210f63..7556dceb 100644 --- a/Lua/src/screens/UScreenOptionsSound.pas +++ b/Lua/src/screens/UScreenOptionsSound.pas @@ -34,32 +34,41 @@ interface {$I switches.inc} uses - UMenu, SDL, UDisplay, UMusic, UFiles, UIni, UThemes; + SDL, + UMenu, + UDisplay, + UMusic, + UFiles, + UIni, + UThemes; type TScreenOptionsSound = class(TMenu) public constructor Create; override; - function ParseInput(PressedKey: cardinal; CharCode: widechar; + function ParseInput(PressedKey: Cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; override; - procedure onShow; override; + procedure OnShow; override; end; implementation -uses UGraphic, SysUtils; +uses + UGraphic, + UUnicodeUtils, + SysUtils; function TScreenOptionsSound.ParseInput(PressedKey: cardinal; - CharCode: widechar; PressedDown: boolean): boolean; + CharCode: UCS4Char; PressedDown: boolean): boolean; begin - Result := True; + Result := true; if (PressedDown) then begin // Key Down // check normal keys - case WideCharUpperCase(CharCode)[1] of - 'Q': + case UCS4UpperCase(CharCode) of + Ord('Q'): begin - Result := False; + Result := false; Exit; end; end; @@ -128,19 +137,39 @@ begin LoadFromTheme(Theme.OptionsSound); - AddSelectSlide(Theme.OptionsSound.SelectSlideVoicePassthrough, - Ini.VoicePassthrough, IVoicePassthrough); - AddSelectSlide(Theme.OptionsSound.SelectBackgroundMusic, - Ini.BackgroundMusicOption, IBackgroundMusic); - AddSelectSlide(Theme.OptionsSound.SelectMicBoost, Ini.MicBoost, IMicBoost); + Theme.OptionsSound.SelectSlideVoicePassthrough.showArrows := true; + Theme.OptionsSound.SelectSlideVoicePassthrough.oneItemOnly := true; + AddSelectSlide(Theme.OptionsSound.SelectSlideVoicePassthrough, Ini.VoicePassthrough, IVoicePassthroughTranslated); + + Theme.OptionsSound.SelectBackgroundMusic.showArrows := true; + Theme.OptionsSound.SelectBackgroundMusic.oneItemOnly := true; + AddSelectSlide(Theme.OptionsSound.SelectBackgroundMusic, Ini.BackgroundMusicOption, IBackgroundMusicTranslated); + // TODO: - MicBoost needs to be moved to ScreenOptionsRecord - AddSelectSlide(Theme.OptionsSound.SelectClickAssist, Ini.ClickAssist, IClickAssist); - AddSelectSlide(Theme.OptionsSound.SelectBeatClick, Ini.BeatClick, IBeatClick); + Theme.OptionsSound.SelectMicBoost.showArrows := true; + Theme.OptionsSound.SelectMicBoost.oneItemOnly := true; + AddSelectSlide(Theme.OptionsSound.SelectMicBoost, Ini.MicBoost, IMicBoostTranslated); + + + Theme.OptionsSound.SelectClickAssist.showArrows := true; + Theme.OptionsSound.SelectClickAssist.oneItemOnly := true; + AddSelectSlide(Theme.OptionsSound.SelectClickAssist, Ini.ClickAssist, IClickAssistTranslated); + + Theme.OptionsSound.SelectBeatClick.showArrows := true; + Theme.OptionsSound.SelectBeatClick.oneItemOnly := true; + AddSelectSlide(Theme.OptionsSound.SelectBeatClick, Ini.BeatClick, IBeatClickTranslated); + + Theme.OptionsSound.SelectThreshold.showArrows := true; + Theme.OptionsSound.SelectThreshold.oneItemOnly := true; AddSelectSlide(Theme.OptionsSound.SelectThreshold, Ini.ThresholdIndex, IThreshold); - AddSelectSlide(Theme.OptionsSound.SelectSlidePreviewVolume, - Ini.PreviewVolume, IPreviewVolume); - AddSelectSlide(Theme.OptionsSound.SelectSlidePreviewFading, - Ini.PreviewFading, IPreviewFading); + + Theme.OptionsSound.SelectSlidePreviewVolume.showArrows := true; + Theme.OptionsSound.SelectSlidePreviewVolume.oneItemOnly := true; + AddSelectSlide(Theme.OptionsSound.SelectSlidePreviewVolume, Ini.PreviewVolume, IPreviewVolumeTranslated); + + Theme.OptionsSound.SelectSlidePreviewFading.showArrows := true; + Theme.OptionsSound.SelectSlidePreviewFading.oneItemOnly := true; + AddSelectSlide(Theme.OptionsSound.SelectSlidePreviewFading, Ini.PreviewFading, IPreviewFadingTranslated); AddButton(Theme.OptionsSound.ButtonExit); if (Length(Button[0].Text) = 0) then @@ -149,7 +178,7 @@ begin Interaction := 0; end; -procedure TScreenOptionsSound.onShow; +procedure TScreenOptionsSound.OnShow; begin inherited; Interaction := 0; diff --git a/Lua/src/screens/UScreenOptionsThemes.pas b/Lua/src/screens/UScreenOptionsThemes.pas index ec6fe014..dca581a2 100644 --- a/Lua/src/screens/UScreenOptionsThemes.pas +++ b/Lua/src/screens/UScreenOptionsThemes.pas @@ -47,29 +47,32 @@ type private procedure ReloadTheme; public - SkinSelect: Integer; + SkinSelect: integer; constructor Create; override; - function ParseInput(PressedKey: Cardinal; CharCode: WideChar; PressedDown: Boolean): Boolean; override; - procedure onShow; override; + function ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; override; + procedure OnShow; override; procedure InteractInc; override; procedure InteractDec; override; end; implementation -uses UMain, - UGraphic, - USkins, - SysUtils; +uses + SysUtils, + UGraphic, + UMain, + UPathUtils, + UUnicodeUtils, + USkins; -function TScreenOptionsThemes.ParseInput(PressedKey: Cardinal; CharCode: WideChar; PressedDown: Boolean): Boolean; +function TScreenOptionsThemes.ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; begin Result := true; - If (PressedDown) Then + if (PressedDown) then begin // Key Down // check normal keys - case WideCharUpperCase(CharCode)[1] of - 'Q': + case UCS4UpperCase(CharCode) of + Ord('Q'): begin Result := false; Exit; @@ -133,7 +136,7 @@ begin if (SelInteraction = 0) then begin Skin.OnThemeChange; - UpdateSelectSlideOptions (Theme.OptionsThemes.SelectSkin, SkinSelect, ISkin, Ini.SkinNo); + UpdateSelectSlideOptions(Theme.OptionsThemes.SelectSkin, SkinSelect, ISkin, Ini.SkinNo); end; ReloadTheme(); @@ -159,18 +162,24 @@ begin LoadFromTheme(Theme.OptionsThemes); + Theme.OptionsThemes.SelectTheme.showArrows := true; + Theme.OptionsThemes.SelectTheme.oneItemOnly := true; AddSelectSlide(Theme.OptionsThemes.SelectTheme, Ini.Theme, ITheme); + Theme.OptionsThemes.SelectSkin.showArrows := true; + Theme.OptionsThemes.SelectSkin.oneItemOnly := true; SkinSelect := AddSelectSlide(Theme.OptionsThemes.SelectSkin, Ini.SkinNo, ISkin); - AddSelectSlide(Theme.OptionsThemes.SelectColor, Ini.Color, IColor); + Theme.OptionsThemes.SelectColor.showArrows := true; + Theme.OptionsThemes.SelectColor.oneItemOnly := true; + AddSelectSlide(Theme.OptionsThemes.SelectColor, Ini.Color, IColorTranslated); AddButton(Theme.OptionsThemes.ButtonExit); if (Length(Button[0].Text)=0) then AddButtonText(14, 20, Theme.Options.Description[7]); end; -procedure TScreenOptionsThemes.onShow; +procedure TScreenOptionsThemes.OnShow; begin inherited; @@ -179,7 +188,7 @@ end; procedure TScreenOptionsThemes.ReloadTheme; begin - Theme.LoadTheme(ThemePath + ITheme[Ini.Theme] + '.ini', Ini.Color); + Theme.LoadTheme(ThemePath.Append(ITheme[Ini.Theme] + '.ini'), Ini.Color); ScreenOptionsThemes := TScreenOptionsThemes.create(); ScreenOptionsThemes.onshow; @@ -188,7 +197,6 @@ begin ScreenOptionsThemes.Interaction := self.Interaction; ScreenOptionsThemes.Draw; - Display.Draw; SwapBuffers; diff --git a/Lua/src/screens/UScreenPartyNewRound.pas b/Lua/src/screens/UScreenPartyNewRound.pas index e0c8c567..f5e51a0d 100644 --- a/Lua/src/screens/UScreenPartyNewRound.pas +++ b/Lua/src/screens/UScreenPartyNewRound.pas @@ -34,71 +34,80 @@ interface {$I switches.inc} uses - UMenu, SDL, UDisplay, UMusic, UFiles, SysUtils, UThemes; + SDL, + SysUtils, + UMenu, + UDisplay, + UMusic, + UFiles, + UThemes; type TScreenPartyNewRound = class(TMenu) public //Texts: - TextRound: array [0..6] of Cardinal; + TextRound: array [0..6] of cardinal; - TextWinner: array [0..6] of Cardinal; + TextWinner: array [0..6] of cardinal; - TextNextRound: Cardinal; - TextNextRoundNo: Cardinal; - TextNextPlayer1: Cardinal; - TextNextPlayer2: Cardinal; - TextNextPlayer3: Cardinal; + TextNextRound: cardinal; + TextNextRoundNo: cardinal; + TextNextPlayer1: cardinal; + TextNextPlayer2: cardinal; + TextNextPlayer3: cardinal; //Statics - StaticRound: array [0..6] of Cardinal; + StaticRound: array [0..6] of cardinal; //Scores - TextScoreTeam1: Cardinal; - TextScoreTeam2: Cardinal; - TextScoreTeam3: Cardinal; - TextNameTeam1: Cardinal; - TextNameTeam2: Cardinal; - TextNameTeam3: Cardinal; + TextScoreTeam1: cardinal; + TextScoreTeam2: cardinal; + TextScoreTeam3: cardinal; + TextNameTeam1: cardinal; + TextNameTeam2: cardinal; + TextNameTeam3: cardinal; - TextTeam1Players: Cardinal; - TextTeam2Players: Cardinal; - TextTeam3Players: Cardinal; + TextTeam1Players: cardinal; + TextTeam2Players: cardinal; + TextTeam3Players: cardinal; + + StaticTeam1: cardinal; + StaticTeam2: cardinal; + StaticTeam3: cardinal; + StaticNextPlayer1: cardinal; + StaticNextPlayer2: cardinal; + StaticNextPlayer3: cardinal; - StaticTeam1: Cardinal; - StaticTeam2: Cardinal; - StaticTeam3: Cardinal; - StaticNextPlayer1: Cardinal; - StaticNextPlayer2: Cardinal; - StaticNextPlayer3: Cardinal; constructor Create; override; - function ParseInput(PressedKey: Cardinal; CharCode: WideChar; PressedDown: Boolean): Boolean; override; - procedure onShow; override; + function ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; override; + procedure OnShow; override; procedure SetAnimationProgress(Progress: real); override; end; implementation -uses UGraphic, - UMain, - UIni, - UTexture, - UParty, - UDLLManager, - ULanguage, - USong, - ULog; - -function TScreenPartyNewRound.ParseInput(PressedKey: Cardinal; CharCode: WideChar; PressedDown: Boolean): Boolean; +uses + UGraphic, + UMain, + UIni, + UTexture, + UParty, + UDLLManager, + ULanguage, + USong, + ULog, + UUnicodeUtils; + +function TScreenPartyNewRound.ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; begin Result := true; - If (PressedDown) Then + if (PressedDown) then begin // Key Down // check normal keys - case WideCharUpperCase(CharCode)[1] of - 'Q': + case UCS4UpperCase(CharCode) of + Ord('Q'): begin Result := false; Exit; @@ -180,21 +189,21 @@ begin LoadFromTheme(Theme.PartyNewRound); end; -procedure TScreenPartyNewRound.onShow; +procedure TScreenPartyNewRound.OnShow; var - I: Integer; - function GetTeamPlayers(const Num: Integer): String; + I: integer; + function GetTeamPlayers(const Num: integer): UTF8String; var - Players: Array of String; - J: Integer; - begin + Players: array of UTF8String; + J: integer; + begin if (Num > High(Party.Teams)) or (Num < 0) then exit; - //Create Players Array + //Create Players array SetLength(Players, Length(Party.Teams[Num].Players)); For J := 0 to High(Party.Teams[Num].Players) do - Players[J] := Party.Teams[Num].Players[J].Name; + Players[J] := UTF8String(Party.Teams[Num].Players[J].Name); //Implode and Return Result := Language.Implode(Players); @@ -228,64 +237,64 @@ begin if (Length(Party.Teams) >= 1) then begin Text[TextScoreTeam1].Text := InttoStr(Party.Teams[0].Score); - Text[TextNameTeam1].Text := Party.Teams[0].Name; + Text[TextNameTeam1].Text := UTF8String(Party.Teams[0].Name); Text[TextTeam1Players].Text := GetTeamPlayers(0); - Text[TextScoreTeam1].Visible := True; - Text[TextNameTeam1].Visible := True; - Text[TextTeam1Players].Visible := True; - Static[StaticTeam1].Visible := True; - Static[StaticNextPlayer1].Visible := True; + Text[TextScoreTeam1].Visible := true; + Text[TextNameTeam1].Visible := true; + Text[TextTeam1Players].Visible := true; + Static[StaticTeam1].Visible := true; + Static[StaticNextPlayer1].Visible := true; end else begin - Text[TextScoreTeam1].Visible := False; - Text[TextNameTeam1].Visible := False; - Text[TextTeam1Players].Visible := False; - Static[StaticTeam1].Visible := False; - Static[StaticNextPlayer1].Visible := False; + Text[TextScoreTeam1].Visible := false; + Text[TextNameTeam1].Visible := false; + Text[TextTeam1Players].Visible := false; + Static[StaticTeam1].Visible := false; + Static[StaticNextPlayer1].Visible := false; end; if (Length(Party.Teams) >= 2) then begin Text[TextScoreTeam2].Text := InttoStr(Party.Teams[1].Score); - Text[TextNameTeam2].Text := Party.Teams[1].Name; + Text[TextNameTeam2].Text := UTF8String(Party.Teams[1].Name); Text[TextTeam2Players].Text := GetTeamPlayers(1); - Text[TextScoreTeam2].Visible := True; - Text[TextNameTeam2].Visible := True; - Text[TextTeam2Players].Visible := True; - Static[StaticTeam2].Visible := True; - Static[StaticNextPlayer2].Visible := True; + Text[TextScoreTeam2].Visible := true; + Text[TextNameTeam2].Visible := true; + Text[TextTeam2Players].Visible := true; + Static[StaticTeam2].Visible := true; + Static[StaticNextPlayer2].Visible := true; end else begin - Text[TextScoreTeam2].Visible := False; - Text[TextNameTeam2].Visible := False; - Text[TextTeam2Players].Visible := False; - Static[StaticTeam2].Visible := False; - Static[StaticNextPlayer2].Visible := False; + Text[TextScoreTeam2].Visible := false; + Text[TextNameTeam2].Visible := false; + Text[TextTeam2Players].Visible := false; + Static[StaticTeam2].Visible := false; + Static[StaticNextPlayer2].Visible := false; end; if (Length(Party.Teams) >= 3) then begin Text[TextScoreTeam3].Text := InttoStr(Party.Teams[2].Score); - Text[TextNameTeam3].Text := Party.Teams[2].Name; + Text[TextNameTeam3].Text := UTF8String(Party.Teams[2].Name); Text[TextTeam3Players].Text := GetTeamPlayers(2); - Text[TextScoreTeam3].Visible := True; - Text[TextNameTeam3].Visible := True; - Text[TextTeam3Players].Visible := True; - Static[StaticTeam3].Visible := True; - Static[StaticNextPlayer3].Visible := True; + Text[TextScoreTeam3].Visible := true; + Text[TextNameTeam3].Visible := true; + Text[TextTeam3Players].Visible := true; + Static[StaticTeam3].Visible := true; + Static[StaticNextPlayer3].Visible := true; end else begin - Text[TextScoreTeam3].Visible := False; - Text[TextNameTeam3].Visible := False; - Text[TextTeam3Players].Visible := False; - Static[StaticTeam3].Visible := False; - Static[StaticNextPlayer3].Visible := False; + Text[TextScoreTeam3].Visible := false; + Text[TextNameTeam3].Visible := false; + Text[TextTeam3Players].Visible := false; + Static[StaticTeam3].Visible := false; + Static[StaticNextPlayer3].Visible := false; end; //nextRound Texts @@ -294,26 +303,26 @@ begin if (Length(Party.Teams) >= 1) then begin Text[TextNextPlayer1].Text := Party.Teams[0].Players[Party.Teams[0].NextPlayer].Name; - Text[TextNextPlayer1].Visible := True; + Text[TextNextPlayer1].Visible := true; end else - Text[TextNextPlayer1].Visible := False; + Text[TextNextPlayer1].Visible := false; if (Length(Party.Teams) >= 2) then begin Text[TextNextPlayer2].Text := Party.Teams[1].Players[Party.Teams[1].NextPlayer].Name; - Text[TextNextPlayer2].Visible := True; + Text[TextNextPlayer2].Visible := true; end else - Text[TextNextPlayer2].Visible := False; + Text[TextNextPlayer2].Visible := false; if (Length(Party.Teams) >= 3) then begin Text[TextNextPlayer3].Text := Party.Teams[2].Players[Party.Teams[2].NextPlayer].Name; - Text[TextNextPlayer3].Visible := True; + Text[TextNextPlayer3].Visible := true; end else - Text[TextNextPlayer3].Visible := False; + Text[TextNextPlayer3].Visible := false; end; procedure TScreenPartyNewRound.SetAnimationProgress(Progress: real); diff --git a/Lua/src/screens/UScreenPartyOptions.pas b/Lua/src/screens/UScreenPartyOptions.pas index 5e2c1d5a..9bca6400 100644 --- a/Lua/src/screens/UScreenPartyOptions.pas +++ b/Lua/src/screens/UScreenPartyOptions.pas @@ -61,20 +61,20 @@ type NumPlayer1, NumPlayer2, NumPlayer3: integer; constructor Create; override; - function ParseInput(PressedKey: cardinal; CharCode: WideChar; PressedDown: boolean): boolean; override; - procedure onShow; override; + function ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; override; + procedure OnShow; override; procedure SetAnimationProgress(Progress: real); override; procedure SetPlaylist2; end; var - IPlaylist: array[0..2] of string; - IPlaylist2: array of string; + IPlaylist: array[0..2] of UTF8String; + IPlaylist2: array of UTF8String; const - ITeams: array[0..1] of string = ('2', '3'); - IPlayers: array[0..3] of string = ('1', '2', '3', '4'); - IRounds: array[0..5] of string = ('2', '3', '4', '5', '6', '7'); + ITeams: array[0..1] of UTF8String = ('2', '3'); + IPlayers: array[0..3] of UTF8String = ('1', '2', '3', '4'); + IRounds: array[0..5] of UTF8String = ('2', '3', '4', '5', '6', '7'); implementation @@ -88,9 +88,10 @@ uses USong, UDLLManager, UPlaylist, - USongs; + USongs, + UUnicodeUtils; -function TScreenPartyOptions.ParseInput(PressedKey: cardinal; CharCode: WideChar; PressedDown: boolean): boolean; +function TScreenPartyOptions.ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; var I, J: integer; OnlyMultiPlayer: boolean; @@ -99,8 +100,8 @@ begin if (PressedDown) then begin // Key Down // check normal keys - case WideCharUpperCase(CharCode)[1] of - 'Q': + case UCS4UpperCase(CharCode) of + Ord('Q'): begin Result := false; Exit; @@ -121,17 +122,26 @@ begin //Don'T start when Playlist is Selected and there are no Playlists if (Playlist = 2) and (Length(PlaylistMan.Playlists) = 0) then Exit; - + {// Don't start when SinglePlayer Teams but only Multiplayer Plugins available + OnlyMultiPlayer := true; + for I := 0 to High(DLLMan.Plugins) do + begin + OnlyMultiPlayer := (OnlyMultiPlayer and DLLMan.Plugins[I].TeamModeOnly); + end; + if (OnlyMultiPlayer) and ((NumPlayer1 = 0) or (NumPlayer2 = 0) or ((NumPlayer3 = 0) and (NumTeams = 1))) then + begin + ScreenPopupError.ShowPopup(Language.Translate('ERROR_NO_PLUGINS')); + Exit; + end;} //Save Difficulty Ini.Difficulty := SelectsS[SelectLevel].SelectedOption; Ini.SaveLevel; - - //Save Num Teams: - {PartySession.Teams.NumTeams := NumTeams + 2; + {//Save Num Teams: + PartySession.Teams.NumTeams := NumTeams + 2; PartySession.Teams.Teaminfo[0].NumPlayers := NumPlayer1+1; PartySession.Teams.Teaminfo[1].NumPlayers := NumPlayer2+1; - PartySession.Teams.Teaminfo[2].NumPlayers := NumPlayer3+1;} + PartySession.Teams.Teaminfo[2].NumPlayers := NumPlayer3+1}; //Save Playlist PlaylistMan.Mode := TSingMode( Playlist ); @@ -159,10 +169,6 @@ begin else PlaylistMan.CurPlayList := Playlist2; - //Start Party - // to-do : Party - //PartySession.StartNewParty(Rounds + 2); - AudioPlayback.PlaySound(SoundLib.Start); //Go to Player Screen FadeTo(@ScreenPartyPlayer); @@ -231,14 +237,14 @@ begin //Load Screen From Theme LoadFromTheme(Theme.PartyOptions); - SelectLevel := AddSelectSlide (Theme.PartyOptions.SelectLevel, Ini.Difficulty, Theme.ILevel); - SelectPlayList := AddSelectSlide (Theme.PartyOptions.SelectPlayList, PlayList, IPlaylist); - SelectPlayList2 := AddSelectSlide (Theme.PartyOptions.SelectPlayList2, PlayList2, IPlaylist2); - SelectRounds := AddSelectSlide (Theme.PartyOptions.SelectRounds, Rounds, IRounds); - SelectTeams := AddSelectSlide (Theme.PartyOptions.SelectTeams, NumTeams, ITeams); - SelectPlayers1 := AddSelectSlide (Theme.PartyOptions.SelectPlayers1, NumPlayer1, IPlayers); - SelectPlayers2 := AddSelectSlide (Theme.PartyOptions.SelectPlayers2, NumPlayer2, IPlayers); - SelectPlayers3 := AddSelectSlide (Theme.PartyOptions.SelectPlayers3, NumPlayer3, IPlayers); + SelectLevel := AddSelectSlide(Theme.PartyOptions.SelectLevel, Ini.Difficulty, Theme.ILevel); + SelectPlayList := AddSelectSlide(Theme.PartyOptions.SelectPlayList, PlayList, IPlaylist); + SelectPlayList2 := AddSelectSlide(Theme.PartyOptions.SelectPlayList2, PlayList2, IPlaylist2); + SelectRounds := AddSelectSlide(Theme.PartyOptions.SelectRounds, Rounds, IRounds); + SelectTeams := AddSelectSlide(Theme.PartyOptions.SelectTeams, NumTeams, ITeams); + SelectPlayers1 := AddSelectSlide(Theme.PartyOptions.SelectPlayers1, NumPlayer1, IPlayers); + SelectPlayers2 := AddSelectSlide(Theme.PartyOptions.SelectPlayers2, NumPlayer2, IPlayers); + SelectPlayers3 := AddSelectSlide(Theme.PartyOptions.SelectPlayers3, NumPlayer3, IPlayers); Interaction := 0; @@ -247,7 +253,8 @@ begin end; procedure TScreenPartyOptions.SetPlaylist2; -var I: integer; +var + I: integer; begin case Playlist of 0: @@ -292,7 +299,7 @@ begin UpdateSelectSlideOptions(Theme.PartyOptions.SelectPlayList2, 2, IPlaylist2, Playlist2); end; -procedure TScreenPartyOptions.onShow; +procedure TScreenPartyOptions.OnShow; begin inherited; diff --git a/Lua/src/screens/UScreenPartyPlayer.pas b/Lua/src/screens/UScreenPartyPlayer.pas index 9a5edca2..d9afe8a0 100644 --- a/Lua/src/screens/UScreenPartyPlayer.pas +++ b/Lua/src/screens/UScreenPartyPlayer.pas @@ -45,38 +45,46 @@ uses type TScreenPartyPlayer = class(TMenu) public - Team1Name: Cardinal; - Player1Name: Cardinal; - Player2Name: Cardinal; - Player3Name: Cardinal; - Player4Name: Cardinal; - - Team2Name: Cardinal; - Player5Name: Cardinal; - Player6Name: Cardinal; - Player7Name: Cardinal; - Player8Name: Cardinal; - - Team3Name: Cardinal; - Player9Name: Cardinal; - Player10Name: Cardinal; - Player11Name: Cardinal; - Player12Name: Cardinal; + Team1Name: cardinal; + Player1Name: cardinal; + Player2Name: cardinal; + Player3Name: cardinal; + Player4Name: cardinal; + + Team2Name: cardinal; + Player5Name: cardinal; + Player6Name: cardinal; + Player7Name: cardinal; + Player8Name: cardinal; + + Team3Name: cardinal; + Player9Name: cardinal; + Player10Name: cardinal; + Player11Name: cardinal; + Player12Name: cardinal; constructor Create; override; - function ParseInput(PressedKey: Cardinal; CharCode: WideChar; PressedDown: Boolean): Boolean; override; - procedure onShow; override; + function ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; override; + procedure OnShow; override; procedure SetAnimationProgress(Progress: real); override; end; implementation -uses UGraphic, UMain, UIni, UTexture, UParty, UScreenPartyOptions, ULanguage; - -function TScreenPartyPlayer.ParseInput(PressedKey: Cardinal; CharCode: WideChar; PressedDown: Boolean): Boolean; +uses + UGraphic, + UMain, + UIni, + UTexture, + UParty, + UUnicodeUtils, + UScreenPartyOptions, + ULanguage; + +function TScreenPartyPlayer.ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; var - SDL_ModState: Word; - I, J: Integer; + SDL_ModState: word; + I, J: integer; HighPlayer: Integer; Rounds: ARounds; procedure IntNext; @@ -103,9 +111,14 @@ begin begin // Key Down // check normal keys case CharCode of - '0'..'9', 'a'..'z', 'A'..'Z', ' ', '-', '_', '!', ',', '<', '/', '*', '?', '''', '"': + Ord('0')..Ord('9'), + Ord('a')..Ord('z'), + Ord('A')..Ord('Z'), + Ord(' '), Ord('-'), Ord('_'), Ord('!'), Ord(','), Ord('<'), Ord('/'), + Ord('*'), Ord('?'), Ord(''''), Ord('"'): begin - Button[Interaction].Text[0].Text := Button[Interaction].Text[0].Text + CharCode; + Button[Interaction].Text[0].Text := Button[Interaction].Text[0].Text + + UCS4ToUTF8String(CharCode); Exit; end; end; @@ -224,7 +237,7 @@ begin SDLK_BACKSPACE: begin - Button[Interaction].Text[0].DeleteLastL; + Button[Interaction].Text[0].DeleteLastLetter; end; SDLK_ESCAPE: @@ -311,8 +324,6 @@ begin end; constructor TScreenPartyPlayer.Create; -//var -// I: integer; // Auto Removed, Unused Variable begin inherited Create; @@ -339,7 +350,7 @@ begin Interaction := 0; end; -procedure TScreenPartyPlayer.onShow; +procedure TScreenPartyPlayer.OnShow; var I: integer; begin @@ -360,7 +371,7 @@ begin Button[10].Text[0].Text := Ini.NameTeam[2]; // Templates for Names Mod end - If (ScreenPartyOptions.NumTeams + 2 >= 1) then + if (ScreenPartyOptions.NumTeams + 2 >= 1) then begin Button[0].Visible := true; Button[1].Visible := (ScreenPartyOptions.NumPlayer1 + 1 >= 1); @@ -370,14 +381,14 @@ begin end else begin - Button[0].Visible := False; - Button[1].Visible := False; - Button[2].Visible := False; - Button[3].Visible := False; - Button[4].Visible := False; + Button[0].Visible := false; + Button[1].Visible := false; + Button[2].Visible := false; + Button[3].Visible := false; + Button[4].Visible := false; end; - If (ScreenPartyOptions.NumTeams + 2 >= 2) then + if (ScreenPartyOptions.NumTeams + 2 >= 2) then begin Button[5].Visible := true; Button[6].Visible := (ScreenPartyOptions.NumPlayer2 + 1 >= 1); @@ -387,14 +398,14 @@ begin end else begin - Button[5].Visible := False; - Button[6].Visible := False; - Button[7].Visible := False; - Button[8].Visible := False; - Button[9].Visible := False; + Button[5].Visible := false; + Button[6].Visible := false; + Button[7].Visible := false; + Button[8].Visible := false; + Button[9].Visible := false; end; - If (ScreenPartyOptions.NumTeams + 2 >= 3) then + if (ScreenPartyOptions.NumTeams + 2 >= 3) then begin Button[10].Visible := true; Button[11].Visible := (ScreenPartyOptions.NumPlayer3 + 1 >= 1); @@ -404,11 +415,11 @@ begin end else begin - Button[10].Visible := False; - Button[11].Visible := False; - Button[12].Visible := False; - Button[13].Visible := False; - Button[14].Visible := False; + Button[10].Visible := false; + Button[11].Visible := false; + Button[12].Visible := false; + Button[13].Visible := false; + Button[14].Visible := false; end; end; diff --git a/Lua/src/screens/UScreenPartyScore.pas b/Lua/src/screens/UScreenPartyScore.pas index eb14a926..e2d4814b 100644 --- a/Lua/src/screens/UScreenPartyScore.pas +++ b/Lua/src/screens/UScreenPartyScore.pas @@ -34,53 +34,66 @@ interface {$I switches.inc} uses - UMenu, SDL, UDisplay, UMusic, SysUtils, UThemes; + SDL, + SysUtils, + UMenu, + UDisplay, + UMusic, + UThemes; type TScreenPartyScore = class(TMenu) public - TextScoreTeam1: Cardinal; - TextScoreTeam2: Cardinal; - TextScoreTeam3: Cardinal; - TextNameTeam1: Cardinal; - TextNameTeam2: Cardinal; - TextNameTeam3: Cardinal; - StaticTeam1: Cardinal; - StaticTeam1BG: Cardinal; - StaticTeam1Deco: Cardinal; - StaticTeam2: Cardinal; - StaticTeam2BG: Cardinal; - StaticTeam2Deco: Cardinal; - StaticTeam3: Cardinal; - StaticTeam3BG: Cardinal; - StaticTeam3Deco: Cardinal; - TextWinner: Cardinal; - - DecoTex: Array[0..5] of Integer; - DecoColor: Array[0..5] of Record - R, G, B: Real; + TextScoreTeam1: cardinal; + TextScoreTeam2: cardinal; + TextScoreTeam3: cardinal; + TextNameTeam1: cardinal; + TextNameTeam2: cardinal; + TextNameTeam3: cardinal; + StaticTeam1: cardinal; + StaticTeam1BG: cardinal; + StaticTeam1Deco: cardinal; + StaticTeam2: cardinal; + StaticTeam2BG: cardinal; + StaticTeam2Deco: cardinal; + StaticTeam3: cardinal; + StaticTeam3BG: cardinal; + StaticTeam3Deco: cardinal; + TextWinner: cardinal; + + DecoTex: array[0..5] of integer; + DecoColor: array[0..5] of Record + R, G, B: real; end; - MaxScore: Word; + MaxScore: word; constructor Create; override; - function ParseInput(PressedKey: Cardinal; CharCode: WideChar; PressedDown: Boolean): Boolean; override; - procedure onShow; override; + function ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; override; + procedure OnShow; override; procedure SetAnimationProgress(Progress: real); override; end; implementation -uses UGraphic, UMain, UParty, UScreenSingModi, ULanguage, UTexture, USkins; - -function TScreenPartyScore.ParseInput(PressedKey: Cardinal; CharCode: WideChar; PressedDown: Boolean): Boolean; +uses + UGraphic, + UMain, + UParty, + UScreenSingModi, + ULanguage, + UTexture, + USkins, + UUnicodeUtils; + +function TScreenPartyScore.ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; begin Result := true; - If (PressedDown) Then + if (PressedDown) then begin // Key Down // check normal keys - case WideCharUpperCase(CharCode)[1] of - 'Q': + case UCS4UpperCase(CharCode) of + Ord('Q'): begin Result := false; Exit; @@ -114,8 +127,8 @@ constructor TScreenPartyScore.Create; var // I: integer; // Auto Removed, Unused Variable Tex: TTexture; - R, G, B: Real; - Color: Integer; + R, G, B: real; + Color: integer; begin inherited Create; @@ -149,7 +162,9 @@ begin DecoColor[0].B := B; //Load Texture - Tex := Texture.LoadTexture(pchar(Skin.GetTextureFileName(Theme.PartyScore.DecoTextures.FirstTexture)), Theme.PartyScore.DecoTextures.FirstTyp, Color); + Tex := Texture.LoadTexture( + Skin.GetTextureFileName(Theme.PartyScore.DecoTextures.FirstTexture), + Theme.PartyScore.DecoTextures.FirstTyp, Color); DecoTex[0] := Tex.TexNum; //Get Second Color @@ -160,7 +175,9 @@ begin DecoColor[1].B := B; //Load Second Texture - Tex := Texture.LoadTexture(pchar(Skin.GetTextureFileName(Theme.PartyScore.DecoTextures.SecondTexture)), Theme.PartyScore.DecoTextures.SecondTyp, Color); + Tex := Texture.LoadTexture( + Skin.GetTextureFileName(Theme.PartyScore.DecoTextures.SecondTexture), + Theme.PartyScore.DecoTextures.SecondTyp, Color); DecoTex[1] := Tex.TexNum; //Get Third Color @@ -171,16 +188,18 @@ begin DecoColor[2].B := B; //Load Third Texture - Tex := Texture.LoadTexture(pchar(Skin.GetTextureFileName(Theme.PartyScore.DecoTextures.ThirdTexture)), Theme.PartyScore.DecoTextures.ThirdTyp, Color); + Tex := Texture.LoadTexture( + Skin.GetTextureFileName(Theme.PartyScore.DecoTextures.ThirdTexture), + Theme.PartyScore.DecoTextures.ThirdTyp, Color); DecoTex[2] := Tex.TexNum; end; LoadFromTheme(Theme.PartyScore); end; -procedure TScreenPartyScore.onShow; +procedure TScreenPartyScore.OnShow; var - I, J: Integer; + I, J: integer; Ranking: AParty_TeamRanking; begin inherited; @@ -208,7 +227,7 @@ begin if (Length(Party.Teams) >= 1) then begin Text[TextScoreTeam1].Text := InttoStr(Party.Teams[0].Score); - Text[TextNameTeam1].Text := Party.Teams[0].Name; + Text[TextNameTeam1].Text := Utf8String(Party.Teams[0].Name); //Set Deco Texture if Theme.PartyScore.DecoTextures.ChangeTextures then @@ -222,25 +241,25 @@ begin end; end; - Text[TextScoreTeam1].Visible := True; - Text[TextNameTeam1].Visible := True; - Static[StaticTeam1].Visible := True; - Static[StaticTeam1BG].Visible := True; - Static[StaticTeam1Deco].Visible := True; + Text[TextScoreTeam1].Visible := true; + Text[TextNameTeam1].Visible := true; + Static[StaticTeam1].Visible := true; + Static[StaticTeam1BG].Visible := true; + Static[StaticTeam1Deco].Visible := true; end else begin - Text[TextScoreTeam1].Visible := False; - Text[TextNameTeam1].Visible := False; - Static[StaticTeam1].Visible := False; - Static[StaticTeam1BG].Visible := False; - Static[StaticTeam1Deco].Visible := False; + Text[TextScoreTeam1].Visible := false; + Text[TextNameTeam1].Visible := false; + Static[StaticTeam1].Visible := false; + Static[StaticTeam1BG].Visible := false; + Static[StaticTeam1Deco].Visible := false; end; if (Length(Party.Teams) >= 2) then begin Text[TextScoreTeam2].Text := InttoStr(Party.Teams[1].Score); - Text[TextNameTeam2].Text := Party.Teams[1].Name; + Text[TextNameTeam2].Text := UTF8String(Party.Teams[1].Name); //Set Deco Texture if Theme.PartyScore.DecoTextures.ChangeTextures then @@ -254,25 +273,25 @@ begin end; end; - Text[TextScoreTeam2].Visible := True; - Text[TextNameTeam2].Visible := True; - Static[StaticTeam2].Visible := True; - Static[StaticTeam2BG].Visible := True; - Static[StaticTeam2Deco].Visible := True; + Text[TextScoreTeam2].Visible := true; + Text[TextNameTeam2].Visible := true; + Static[StaticTeam2].Visible := true; + Static[StaticTeam2BG].Visible := true; + Static[StaticTeam2Deco].Visible := true; end else begin - Text[TextScoreTeam2].Visible := False; - Text[TextNameTeam2].Visible := False; - Static[StaticTeam2].Visible := False; - Static[StaticTeam2BG].Visible := False; - Static[StaticTeam2Deco].Visible := False; + Text[TextScoreTeam2].Visible := false; + Text[TextNameTeam2].Visible := false; + Static[StaticTeam2].Visible := false; + Static[StaticTeam2BG].Visible := false; + Static[StaticTeam2Deco].Visible := false; end; if (Length(Party.Teams) >= 3) then begin Text[TextScoreTeam3].Text := InttoStr(Party.Teams[2].Score); - Text[TextNameTeam3].Text := Party.Teams[2].Name; + Text[TextNameTeam3].Text := UTF8String(Party.Teams[2].Name); //Set Deco Texture if Theme.PartyScore.DecoTextures.ChangeTextures then @@ -286,19 +305,19 @@ begin end; end; - Text[TextScoreTeam3].Visible := True; - Text[TextNameTeam3].Visible := True; - Static[StaticTeam3].Visible := True; - Static[StaticTeam3BG].Visible := True; - Static[StaticTeam3Deco].Visible := True; + Text[TextScoreTeam3].Visible := true; + Text[TextNameTeam3].Visible := true; + Static[StaticTeam3].Visible := true; + Static[StaticTeam3BG].Visible := true; + Static[StaticTeam3Deco].Visible := true; end else begin - Text[TextScoreTeam3].Visible := False; - Text[TextNameTeam3].Visible := False; - Static[StaticTeam3].Visible := False; - Static[StaticTeam3BG].Visible := False; - Static[StaticTeam3Deco].Visible := False; + Text[TextScoreTeam3].Visible := false; + Text[TextNameTeam3].Visible := false; + Static[StaticTeam3].Visible := false; + Static[StaticTeam3BG].Visible := false; + Static[StaticTeam3Deco].Visible := false; end; end; @@ -313,4 +332,3 @@ begin end; end. - diff --git a/Lua/src/screens/UScreenPartyWin.pas b/Lua/src/screens/UScreenPartyWin.pas index 280a9b28..18a6e69e 100644 --- a/Lua/src/screens/UScreenPartyWin.pas +++ b/Lua/src/screens/UScreenPartyWin.pas @@ -34,46 +34,57 @@ interface {$I switches.inc} uses - UMenu, SDL, UDisplay, UMusic, SysUtils, UThemes; + SDL, + SysUtils, + UMenu, + UDisplay, + UMusic, + UThemes; type TScreenPartyWin = class(TMenu) public - TextScoreTeam1: Cardinal; - TextScoreTeam2: Cardinal; - TextScoreTeam3: Cardinal; - TextNameTeam1: Cardinal; - TextNameTeam2: Cardinal; - TextNameTeam3: Cardinal; - StaticTeam1: Cardinal; - StaticTeam1BG: Cardinal; - StaticTeam1Deco: Cardinal; - StaticTeam2: Cardinal; - StaticTeam2BG: Cardinal; - StaticTeam2Deco: Cardinal; - StaticTeam3: Cardinal; - StaticTeam3BG: Cardinal; - StaticTeam3Deco: Cardinal; - TextWinner: Cardinal; + TextScoreTeam1: cardinal; + TextScoreTeam2: cardinal; + TextScoreTeam3: cardinal; + TextNameTeam1: cardinal; + TextNameTeam2: cardinal; + TextNameTeam3: cardinal; + StaticTeam1: cardinal; + StaticTeam1BG: cardinal; + StaticTeam1Deco: cardinal; + StaticTeam2: cardinal; + StaticTeam2BG: cardinal; + StaticTeam2Deco: cardinal; + StaticTeam3: cardinal; + StaticTeam3BG: cardinal; + StaticTeam3Deco: cardinal; + TextWinner: cardinal; constructor Create; override; - function ParseInput(PressedKey: Cardinal; CharCode: WideChar; PressedDown: Boolean): Boolean; override; - procedure onShow; override; + function ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; override; + procedure OnShow; override; procedure SetAnimationProgress(Progress: real); override; end; implementation -uses UGraphic, UMain, UParty, UScreenSingModi, ULanguage; - -function TScreenPartyWin.ParseInput(PressedKey: Cardinal; CharCode: WideChar; PressedDown: Boolean): Boolean; +uses + UGraphic, + UMain, + UParty, + UScreenSingModi, + ULanguage, + UUnicodeUtils; + +function TScreenPartyWin.ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; begin Result := true; - If (PressedDown) Then + if (PressedDown) then begin // Key Down // check normal keys - case WideCharUpperCase(CharCode)[1] of - 'Q': + case UCS4UpperCase(CharCode) of + Ord('Q'): begin Result := false; Exit; @@ -121,14 +132,14 @@ begin LoadFromTheme(Theme.PartyWin); end; -procedure TScreenPartyWin.onShow; +procedure TScreenPartyWin.OnShow; var - I, J: Integer; + I, J: integer; Ranking: AParty_TeamRanking; - Function GetTeamColor(Team: Integer): Cardinal; + Function GetTeamColor(Team: integer): cardinal; var - NameString: String; + NameString: string; begin NameString := 'P' + InttoStr(Team+1) + 'Dark'; @@ -149,14 +160,14 @@ begin Text[TextScoreTeam1].Text := IntToStr(Party.Teams[Ranking[0].Team].Score); Text[TextNameTeam1].Text := Party.Teams[Ranking[0].Team].Name; - Text[TextScoreTeam1].Visible := True; - Text[TextNameTeam1].Visible := True; - Static[StaticTeam1].Visible := True; - Static[StaticTeam1BG].Visible := True; - Static[StaticTeam1Deco].Visible := True; + Text[TextScoreTeam1].Visible := true; + Text[TextNameTeam1].Visible := true; + Static[StaticTeam1].Visible := true; + Static[StaticTeam1BG].Visible := true; + Static[StaticTeam1Deco].Visible := true; //Set Static Color to Team Color - If (Theme.PartyWin.StaticTeam1BG.Color = 'TeamColor') then + if (Theme.PartyWin.StaticTeam1BG.Color = 'TeamColor') then begin I := GetTeamColor(Ranking[0].Team); if (I <> -1) then @@ -167,7 +178,7 @@ begin end; end; - If (Theme.PartyWin.StaticTeam1.Color = 'TeamColor') then + if (Theme.PartyWin.StaticTeam1.Color = 'TeamColor') then begin I := GetTeamColor(Ranking[0].Team); if (I <> -1) then @@ -180,11 +191,11 @@ begin end else begin - Text[TextScoreTeam1].Visible := False; - Text[TextNameTeam1].Visible := False; - Static[StaticTeam1].Visible := False; - Static[StaticTeam1BG].Visible := False; - Static[StaticTeam1Deco].Visible := False; + Text[TextScoreTeam1].Visible := false; + Text[TextNameTeam1].Visible := false; + Static[StaticTeam1].Visible := false; + Static[StaticTeam1BG].Visible := false; + Static[StaticTeam1Deco].Visible := false; end; if (Length(Party.Teams) >= 2) then @@ -192,14 +203,14 @@ begin Text[TextScoreTeam2].Text := IntToStr(Party.Teams[Ranking[1].Team].Score); Text[TextNameTeam2].Text := Party.Teams[Ranking[1].Team].Name; - Text[TextScoreTeam2].Visible := True; - Text[TextNameTeam2].Visible := True; - Static[StaticTeam2].Visible := True; - Static[StaticTeam2BG].Visible := True; - Static[StaticTeam2Deco].Visible := True; + Text[TextScoreTeam2].Visible := true; + Text[TextNameTeam2].Visible := true; + Static[StaticTeam2].Visible := true; + Static[StaticTeam2BG].Visible := true; + Static[StaticTeam2Deco].Visible := true; //Set Static Color to Team Color - If (Theme.PartyWin.StaticTeam2BG.Color = 'TeamColor') then + if (Theme.PartyWin.StaticTeam2BG.Color = 'TeamColor') then begin I := GetTeamColor(Ranking[1].Team); if (I <> -1) then @@ -210,7 +221,7 @@ begin end; end; - If (Theme.PartyWin.StaticTeam2.Color = 'TeamColor') then + if (Theme.PartyWin.StaticTeam2.Color = 'TeamColor') then begin I := GetTeamColor(Ranking[1].Team); if (I <> -1) then @@ -223,11 +234,11 @@ begin end else begin - Text[TextScoreTeam2].Visible := False; - Text[TextNameTeam2].Visible := False; - Static[StaticTeam2].Visible := False; - Static[StaticTeam2BG].Visible := False; - Static[StaticTeam2Deco].Visible := False; + Text[TextScoreTeam2].Visible := false; + Text[TextNameTeam2].Visible := false; + Static[StaticTeam2].Visible := false; + Static[StaticTeam2BG].Visible := false; + Static[StaticTeam2Deco].Visible := false; end; if (Length(Party.Teams) >= 3) then @@ -235,14 +246,14 @@ begin Text[TextScoreTeam3].Text := IntToStr(Party.Teams[Ranking[2].Team].Score); Text[TextNameTeam3].Text := Party.Teams[Ranking[2].Team].Name; - Text[TextScoreTeam3].Visible := True; - Text[TextNameTeam3].Visible := True; - Static[StaticTeam3].Visible := True; - Static[StaticTeam3BG].Visible := True; - Static[StaticTeam3Deco].Visible := True; + Text[TextScoreTeam3].Visible := true; + Text[TextNameTeam3].Visible := true; + Static[StaticTeam3].Visible := true; + Static[StaticTeam3BG].Visible := true; + Static[StaticTeam3Deco].Visible := true; //Set Static Color to Team Color - If (Theme.PartyWin.StaticTeam3BG.Color = 'TeamColor') then + if (Theme.PartyWin.StaticTeam3BG.Color = 'TeamColor') then begin I := GetTeamColor(Ranking[2].Team); if (I <> -1) then @@ -253,7 +264,7 @@ begin end; end; - If (Theme.PartyWin.StaticTeam3.Color = 'TeamColor') then + if (Theme.PartyWin.StaticTeam3.Color = 'TeamColor') then begin I := GetTeamColor(Ranking[2].Team); if (I <> -1) then @@ -266,11 +277,11 @@ begin end else begin - Text[TextScoreTeam3].Visible := False; - Text[TextNameTeam3].Visible := False; - Static[StaticTeam3].Visible := False; - Static[StaticTeam3BG].Visible := False; - Static[StaticTeam3Deco].Visible := False; + Text[TextScoreTeam3].Visible := false; + Text[TextNameTeam3].Visible := false; + Static[StaticTeam3].Visible := false; + Static[StaticTeam3BG].Visible := false; + Static[StaticTeam3Deco].Visible := false; end; end; diff --git a/Lua/src/screens/UScreenPopup.pas b/Lua/src/screens/UScreenPopup.pas index b606c306..fdf4a69c 100644 --- a/Lua/src/screens/UScreenPopup.pas +++ b/Lua/src/screens/UScreenPopup.pas @@ -34,106 +34,124 @@ interface {$I switches.inc} uses - UMenu, SDL, UMusic, UFiles, SysUtils, UThemes; + SDL, + SysUtils, + UMenu, + UMusic, + UFiles, + UThemes; type + TPopupCheckHandler = procedure(Value: boolean; Data: Pointer); + TScreenPopupCheck = class(TMenu) + private + fHandler: TPopupCheckHandler; + fHandlerData: Pointer; + public - Visible: Boolean; //Whether the Menu should be Drawn + Visible: boolean; // whether the menu should be drawn constructor Create; override; - function ParseInput(PressedKey: Cardinal; CharCode: WideChar; PressedDown: Boolean): Boolean; override; - procedure onShow; override; - procedure ShowPopup(msg: String); + function ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; override; + procedure OnShow; override; + procedure ShowPopup(const Msg: UTF8String; Handler: TPopupCheckHandler; + HandlerData: Pointer; DefaultValue: boolean = false); function Draw: boolean; override; end; type - TScreenPopupError = class(TMenu) -{ private - CurMenu: Byte; //Num of the cur. Shown Menu} + TScreenPopup = class(TMenu) + { + private + CurMenu: byte; //Num of the cur. Shown Menu + } public - Visible: Boolean; //Whether the Menu should be Drawn + Visible: boolean; //Whether the Menu should be Drawn constructor Create; override; - function ParseInput(PressedKey: Cardinal; CharCode: WideChar; PressedDown: Boolean): Boolean; override; - procedure onShow; override; - procedure onHide; override; - procedure ShowPopup(msg: String); + function ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; override; + procedure OnShow; override; + procedure OnHide; override; + procedure ShowPopup(const Msg: UTF8String); function Draw: boolean; override; end; -var -// ISelections: Array of String; - SelectValue: Integer; + TScreenPopupError = class(TScreenPopup) + public + constructor Create; + end; + TScreenPopupInfo = class(TScreenPopup) + public + constructor Create; + end; -implementation +var + //ISelections: array of string; + SelectValue: integer; -uses UGraphic, UMain, UIni, UTexture, ULanguage, UParty, UPlaylist, UDisplay; +implementation -function TScreenPopupCheck.ParseInput(PressedKey: Cardinal; CharCode: WideChar; PressedDown: Boolean): Boolean; +uses + UGraphic, + UMain, + UIni, + UTexture, + ULanguage, + UParty, + UPlaylist, + UDisplay, + UUnicodeUtils; + +{ TScreenPopupCheck } + +function TScreenPopupCheck.ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; +var + Value: boolean; begin Result := true; - If (PressedDown) Then + if (PressedDown) then begin // Key Down - // check normal keys - case WideCharUpperCase(CharCode)[1] of - 'Q': - begin - Result := false; - Exit; - end; - end; - // check special keys case PressedKey of SDLK_ESCAPE, SDLK_BACKSPACE : begin - Display.CheckOK:=False; - Display.NextScreenWithCheck:=NIL; - Visible:=False; + Value := false; + Visible := false; Result := false; end; SDLK_RETURN: begin - case Interaction of - 0: begin - //Hack to Finish Singscreen correct on Exit with Q Shortcut - if (Display.NextScreenWithCheck = NIL) then - begin - if (Display.CurrentScreen = @ScreenSing) then - ScreenSing.Finish - {else if (Display.CurrentScreen = @ScreenSingModi) then - ScreenSingModi.Finish;} - end; - - Display.CheckOK:=True; - end; - 1: begin - Display.CheckOK:=False; - Display.NextScreenWithCheck:=NIL; - end; - end; - Visible:=False; + Value := (Interaction = 0); + Visible := false; Result := false; end; - SDLK_DOWN: InteractNext; - SDLK_UP: InteractPrev; - + SDLK_DOWN: InteractNext; + SDLK_UP: InteractPrev; + SDLK_RIGHT: InteractNext; - SDLK_LEFT: InteractPrev; + SDLK_LEFT: InteractPrev; end; end; + + if (not Result) then + begin + if (@fHandler <> nil) then + fHandler(Value, fHandlerData); + end; end; constructor TScreenPopupCheck.Create; begin inherited Create; + fHandler := nil; + fHandlerData := nil; + AddText(Theme.CheckPopup.TextCheck); LoadFromTheme(Theme.CheckPopup); @@ -151,23 +169,29 @@ end; function TScreenPopupCheck.Draw: boolean; begin - Draw:=inherited Draw; + Result := inherited Draw; end; -procedure TScreenPopupCheck.onShow; +procedure TScreenPopupCheck.OnShow; begin inherited; end; -procedure TScreenPopupCheck.ShowPopup(msg: String); +procedure TScreenPopupCheck.ShowPopup(const Msg: UTF8String; Handler: TPopupCheckHandler; + HandlerData: Pointer; DefaultValue: boolean); begin - Interaction := 0; //Reset Interaction - Visible := True; //Set Visible + if (DefaultValue) then + Interaction := 0 + else + Interaction := 1; + Visible := true; //Set Visible + fHandler := Handler; + fHandlerData := HandlerData; Text[0].Text := Language.Translate(msg); - Button[0].Visible := True; - Button[1].Visible := True; + Button[0].Visible := true; + Button[1].Visible := true; Button[0].Text[0].Text := Language.Translate('SONG_MENU_YES'); Button[1].Text[0].Text := Language.Translate('SONG_MENU_NO'); @@ -175,12 +199,12 @@ begin Background.OnShow end; -// error popup +{ TScreenPopup } -function TScreenPopupError.ParseInput(PressedKey: Cardinal; CharCode: WideChar; PressedDown: Boolean): Boolean; +function TScreenPopup.ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; begin Result := true; - If (PressedDown) Then + if (PressedDown) then begin // Key Down case PressedKey of @@ -192,13 +216,13 @@ begin SDLK_ESCAPE, SDLK_BACKSPACE : begin - Visible:=False; + Visible := false; Result := false; end; SDLK_RETURN: begin - Visible:=False; + Visible := false; Result := false; end; @@ -211,7 +235,7 @@ begin end; end; -constructor TScreenPopupError.Create; +constructor TScreenPopup.Create; begin inherited Create; @@ -226,43 +250,59 @@ begin Interaction := 0; end; -function TScreenPopupError.Draw: boolean; +function TScreenPopup.Draw: boolean; begin - Draw:=inherited Draw; + Draw := inherited Draw; end; -procedure TScreenPopupError.onShow; +procedure TScreenPopup.OnShow; begin inherited; end; -procedure TScreenPopupError.onHide; +procedure TScreenPopup.OnHide; begin end; -procedure TScreenPopupError.ShowPopup(msg: String); +procedure TScreenPopup.ShowPopup(const Msg: UTF8String); begin Interaction := 0; //Reset Interaction - Visible := True; //Set Visible + Visible := true; //Set Visible Background.OnShow; { //dirty hack... Text[0] is invisible for some strange reason for i:=1 to high(Text) do if i-1 <= high(msg) then begin - Text[i].Visible:=True; + Text[i].Visible := true; Text[i].Text := msg[i-1]; end else begin - Text[i].Visible:=False; + Text[i].Visible := false; end;} - Text[0].Text:=msg; + Text[0].Text := msg; - Button[0].Visible := True; + Button[0].Visible := true; Button[0].Text[0].Text := 'OK'; end; +{ TScreenPopupError } + +constructor TScreenPopupError.Create; +begin + inherited; + Text[1].Text := Language.Translate('MSG_ERROR_TITLE'); +end; + +{ TScreenPopupInfo } + +constructor TScreenPopupInfo.Create; +begin + inherited; + Text[1].Text := Language.Translate('MSG_INFO_TITLE'); +end; + end. diff --git a/Lua/src/screens/UScreenScore.pas b/Lua/src/screens/UScreenScore.pas index ee94d345..ce1b11e5 100644 --- a/Lua/src/screens/UScreenScore.pas +++ b/Lua/src/screens/UScreenScore.pas @@ -46,56 +46,56 @@ uses UTexture; const - ZBars : real = 0.8; // Z value for the bars - ZRatingPic : real = 0.8; // Z value for the rating pictures + ZBars: real = 0.8; // Z value for the bars + ZRatingPic: real = 0.8; // Z value for the rating pictures - EaseOut_MaxSteps : real = 10; // that's the speed of the bars (10 is fast | 100 is slower) + EaseOut_MaxSteps: real = 10; // that's the speed of the bars (10 is fast | 100 is slower) - BarRaiseSpeed : cardinal = 0; // Time for raising the bar one step higher (in ms) + BarRaiseSpeed: cardinal = 0; // Time for raising the bar one step higher (in ms) type TPlayerScoreScreenTexture = record // holds all colorized textures for up to 6 players //Bar textures - Score_NoteBarLevel_Dark : TTexture; // Note - Score_NoteBarRound_Dark : TTexture; // that's the round thing on top + Score_NoteBarLevel_Dark: TTexture; // Note + Score_NoteBarRound_Dark: TTexture; // that's the round thing on top - Score_NoteBarLevel_Light : TTexture; // LineBonus | Phrasebonus - Score_NoteBarRound_Light : TTexture; + Score_NoteBarLevel_Light: TTexture; // LineBonus | Phrasebonus + Score_NoteBarRound_Light: TTexture; - Score_NoteBarLevel_Lightest : TTexture; // GoldenNotes - Score_NoteBarRound_Lightest : TTexture; + Score_NoteBarLevel_Lightest: TTexture; // GoldenNotes + Score_NoteBarRound_Lightest: TTexture; end; TPlayerScoreScreenData = record // holds the positions and other data - Bar_Y :Real; - Bar_Actual_Height : Real; // this one holds the actual height of the bar, while we animate it - BarScore_ActualHeight : Real; - BarLine_ActualHeight : Real; - BarGolden_ActualHeight : Real; + Bar_Y: real; + Bar_Actual_Height: real; // this one holds the actual height of the bar, while we animate it + BarScore_ActualHeight: real; + BarLine_ActualHeight: real; + BarGolden_ActualHeight: real; end; TPlayerScoreRatingPics = record // a fine array of the rating pictures - RateEaseStep : Integer; - RateEaseValue: Real; + RateEaseStep: integer; + RateEaseValue: real; end; TScreenScore = class(TMenu) private - BarTime : Cardinal; - ArrayStartModifier : integer; + BarTime: cardinal; + ArrayStartModifier: integer; public - aPlayerScoreScreenTextures : array[1..6] of TPlayerScoreScreenTexture; - aPlayerScoreScreenDatas : array[1..6] of TPlayerScoreScreenData; - aPlayerScoreScreenRatings : array[1..6] of TPlayerScoreRatingPics; + aPlayerScoreScreenTextures: array[1..6] of TPlayerScoreScreenTexture; + aPlayerScoreScreenDatas: array[1..6] of TPlayerScoreScreenData; + aPlayerScoreScreenRatings: array[1..6] of TPlayerScoreRatingPics; - BarScore_EaseOut_Step : real; - BarPhrase_EaseOut_Step : real; - BarGolden_EaseOut_Step : real; + BarScore_EaseOut_Step: real; + BarPhrase_EaseOut_Step: real; + BarGolden_EaseOut_Step: real; - TextArtist: integer; - TextTitle: integer; + TextArtist: integer; + TextTitle: integer; - TextArtistTitle : integer; + TextArtistTitle: integer; TextName: array[1..6] of integer; TextScore: array[1..6] of integer; @@ -110,66 +110,66 @@ type TextTotalScore: array[1..6] of integer; PlayerStatic: array[1..6] of array of integer; - PlayerTexts : array[1..6] of array of integer; - + PlayerTexts: array[1..6] of array of integer; StaticBoxLightest: array[1..6] of integer; StaticBoxLight: array[1..6] of integer; StaticBoxDark: array[1..6] of integer; - StaticBackLevel: array[1..6] of integer; - StaticBackLevelRound: array[1..6] of integer; - StaticLevel: array[1..6] of integer; - StaticLevelRound: array[1..6] of integer; - - Animation: real; - - TextScore_ActualValue : array[1..6] of integer; - TextPhrase_ActualValue : array[1..6] of integer; - TextGolden_ActualValue : array[1..6] of integer; + StaticBackLevel: array[1..6] of integer; + StaticBackLevelRound: array[1..6] of integer; + StaticLevel: array[1..6] of integer; + StaticLevelRound: array[1..6] of integer; + Animation: real; + TextScore_ActualValue: array[1..6] of integer; + TextPhrase_ActualValue: array[1..6] of integer; + TextGolden_ActualValue: array[1..6] of integer; constructor Create; override; - function ParseInput(PressedKey: Cardinal; CharCode: WideChar; PressedDown: Boolean): Boolean; override; - procedure onShow; override; - procedure onShowFinish; override; + function ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; override; + function ParseMouse(MouseButton: Integer; BtnDown: Boolean; X, Y: integer): boolean; override; + procedure OnShow; override; + procedure OnShowFinish; override; function Draw: boolean; override; procedure FillPlayer(Item, P: integer); - procedure EaseBarIn(PlayerNumber : Integer; BarType: String); - procedure EaseScoreIn(PlayerNumber : Integer; ScoreType: String); - - procedure FillPlayerItems(PlayerNumber : Integer; ScoreType: String); + procedure EaseBarIn(PlayerNumber: integer; BarType: string); + procedure EaseScoreIn(PlayerNumber: integer; ScoreType: string); + procedure FillPlayerItems(PlayerNumber: integer; ScoreType: string); - procedure DrawBar(BarType:string; PlayerNumber: integer; BarStartPosY: single; NewHeight: real); + procedure DrawBar(BarType: string; PlayerNumber: integer; BarStartPosY: single; NewHeight: real); //Rating Picture procedure ShowRating(PlayerNumber: integer); - function CalculateBouncing(PlayerNumber : Integer): real; - procedure DrawRating(PlayerNumber:integer;Rating:integer); + function CalculateBouncing(PlayerNumber: integer): real; + procedure DrawRating(PlayerNumber: integer; Rating: integer); end; implementation - -uses UGraphic, - UScreenSong, - UMenuStatic, - UTime, - UMain, - UIni, - ULog, - ULanguage; - -function TScreenScore.ParseInput(PressedKey: Cardinal; CharCode: WideChar; PressedDown: Boolean): Boolean; +uses + UGraphic, + UScreenSong, + UMenuStatic, + UTime, + UIni, + ULog, + ULanguage, + UNote, + UUnicodeUtils; + + +function TScreenScore.ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; begin Result := true; - If (PressedDown) Then begin + if (PressedDown) then + begin // check normal keys - case WideCharUpperCase(CharCode)[1] of - 'Q': + case UCS4UpperCase(CharCode) of + Ord('Q'): begin Result := false; Exit; @@ -194,6 +194,15 @@ begin end; end; +function TScreenScore.ParseMouse(MouseButton: Integer; BtnDown: Boolean; X, Y: integer): boolean; +begin + Result := True; + if (MouseButton = SDL_BUTTON_LEFT) and BtnDown then begin + //left-click anywhere sends return + ParseInput(SDLK_RETURN, 0, true); + end; +end; + constructor TScreenScore.Create; var Player: integer; @@ -254,11 +263,11 @@ begin end; -procedure TScreenScore.onShow; +procedure TScreenScore.OnShow; var - P: integer; // player - I: integer; - V: array[1..6] of boolean; // visibility array + P: integer; // player + I: integer; + V: array[1..6] of boolean; // visibility array begin @@ -295,7 +304,6 @@ begin aPlayerScoreScreenRatings[P].RateEaseValue := 20; end; - Text[TextArtist].Text := CurrentSong.Artist; Text[TextTitle].Text := CurrentSong.Title; Text[TextArtistTitle].Text := CurrentSong.Artist + ' - ' + CurrentSong.Title; @@ -347,7 +355,6 @@ begin Static[StaticBoxLight[P]].Texture.Alpha := 0; Static[StaticBoxDark[P]].Texture.Alpha := 0; - Text[TextNotes[P]].Visible := V[P]; Text[TextNotesScore[P]].Visible := V[P]; Text[TextLineBonus[P]].Visible := V[P]; @@ -377,14 +384,14 @@ end; procedure TScreenScore.onShowFinish; var - index : integer; + index: integer; begin for index := 1 to (PlayersPlay) do - begin - TextScore_ActualValue[index] := 0; - TextPhrase_ActualValue[index] := 0; - TextGolden_ActualValue[index] := 0; - end; + begin + TextScore_ActualValue[index] := 0; + TextPhrase_ActualValue[index] := 0; + TextGolden_ActualValue[index] := 0; + end; BarScore_EaseOut_Step := 1; BarPhrase_EaseOut_Step := 1; @@ -393,11 +400,11 @@ end; function TScreenScore.Draw: boolean; var - CurrentTime : Cardinal; - PlayerCounter : integer; + CurrentTime: cardinal; + PlayerCounter: integer; + PStart: integer; + PHigh: integer; begin - - inherited Draw; {* player[0].ScoreInt := 7000; player[0].ScoreLineInt := 2000; @@ -409,13 +416,38 @@ begin player[1].ScoreGoldenInt := 900; player[1].ScoreTotalInt := 4500; *} + + //Draw the Background + DrawBG; + + //Calculate first and last Player on this Screen + if (PlayersPlay > 3) then + begin + case PlayersPlay of + 4: begin + PStart := 1 + ((ScreenAct-1) * 2); + PHigh := 2 + ((ScreenAct-1) * 2); + end; + + 6: begin + PStart := 1 + ((ScreenAct-1) * 3); + PHigh := 3 + ((ScreenAct-1) * 3); + end; + end; + end + else + begin + PStart := 1; + PHigh := PlayersPlay; + end; + // Let's start to arise the bars CurrentTime := SDL_GetTicks(); - if((CurrentTime >= BarTime) AND ShowFinish) then + if((CurrentTime >= BarTime) and ShowFinish) then begin BarTime := CurrentTime + BarRaiseSpeed; - for PlayerCounter := 1 to PlayersPlay do + for PlayerCounter := PStart to PHigh do begin // We actually arise them in the right order, but we have to draw them in reverse order (golden -> phrase -> mainscore) if (BarScore_EaseOut_Step < EaseOut_MaxSteps * 10) then @@ -427,7 +459,6 @@ begin if (BarPhrase_EaseOut_Step < EaseOut_MaxSteps * 10) then BarPhrase_EaseOut_Step := BarPhrase_EaseOut_Step + 1; - // GoldenNotebonus if (BarPhrase_EaseOut_Step >= (EaseOut_MaxSteps * 10)) then begin @@ -448,12 +479,25 @@ begin EaseBarIn(PlayerCounter, 'Note'); EaseScoreIn(PlayerCounter,'Note'); - - FillPlayerItems(PlayerCounter,'Funky'); + if (PlayersPlay <= 3) then + //If we play w/ 3 or less players they fit in one screen + //so we don't have to swap the values of themeobjects + //on every draw + FillPlayerItems(PlayerCounter,'Funky'); end; end; + if (PlayersPlay > 3) then + //more then 3 players don't fit the screen + //so we have to swap the themeobjects values on every draw + for PlayerCounter := PStart to PHigh do + begin + FillPlayerItems(PlayerCounter,'Funky'); + end; + + //Draw Theme Objects + DrawFG; (* //todo: i need a clever method to draw statics with their z value @@ -466,7 +510,7 @@ begin Result := true; end; -procedure TscreenScore.FillPlayerItems(PlayerNumber : Integer; ScoreType: String); +procedure TscreenScore.FillPlayerItems(PlayerNumber: integer; ScoreType: string); var ThemeIndex: integer; begin @@ -474,7 +518,13 @@ begin Text[TextName[PlayerNumber + ArrayStartModifier]].Text := Ini.Name[PlayerNumber - 1]; // end todo - ThemeIndex := PlayerNumber + ArrayStartModifier; + // We have to do this here because we use the same Theme Object + // for players on the first and second screen + case PlayersPlay of + 1, 2, 3: ThemeIndex := PlayerNumber + ArrayStartModifier; + 4: ThemeIndex := ((PlayerNumber-1) mod 2) + 1 + ArrayStartModifier; + 6: ThemeIndex := ((PlayerNumber-1) mod 3) + 1 + ArrayStartModifier; + end; //golden Text[TextGoldenNotesScore[ThemeIndex]].Text := IntToStr(TextGolden_ActualValue[PlayerNumber]); @@ -511,14 +561,19 @@ begin end; end; - procedure TScreenScore.ShowRating(PlayerNumber: integer); var - Rating : integer; - ThemeIndex : integer; + Rating: integer; + ThemeIndex: integer; begin - ThemeIndex := PlayerNumber + ArrayStartModifier; + // We have to do this here because we use the same Theme Object + // for players on the first and second screen + case PlayersPlay of + 1, 2, 3: ThemeIndex := PlayerNumber + ArrayStartModifier; + 4: ThemeIndex := ((PlayerNumber-1) mod 2) + 1 + ArrayStartModifier; + 6: ThemeIndex := ((PlayerNumber-1) mod 3) + 1 + ArrayStartModifier; + end; case (Player[PlayerNumber-1].ScoreTotalInt) of 0..2009: @@ -566,7 +621,7 @@ begin end; //todo: this could break if the width is not given, for instance when there's a skin with no picture for ratings - if ( Theme.Score.StaticRatings[ThemeIndex].W > 0 ) AND ( aPlayerScoreScreenRatings[PlayerNumber].RateEaseValue > 0 ) then + if ( Theme.Score.StaticRatings[ThemeIndex].W > 0 ) and ( aPlayerScoreScreenRatings[PlayerNumber].RateEaseValue > 0 ) then begin Text[TextScore[ThemeIndex]].Alpha := aPlayerScoreScreenRatings[PlayerNumber].RateEaseValue / Theme.Score.StaticRatings[ThemeIndex].W; end; @@ -575,11 +630,11 @@ begin DrawRating(PlayerNumber, Rating); end; -procedure TscreenScore.DrawRating(PlayerNumber:integer;Rating:integer); +procedure TscreenScore.DrawRating(PlayerNumber: integer; Rating: integer); var - Posx : real; - Posy : real; - Width :real; + Posx: real; + Posy: real; + Width: real; begin CalculateBouncing(PlayerNumber); @@ -606,56 +661,53 @@ begin glDisable(GL_TEXTURE_2d); end; - - -function TscreenScore.CalculateBouncing(PlayerNumber : Integer): real; +function TscreenScore.CalculateBouncing(PlayerNumber: integer): real; var - ReturnValue : real; - p, s : real; + ReturnValue: real; + p, s: real; - RaiseStep, MaxVal : real; - EaseOut_Step : integer; + RaiseStep, MaxVal: real; + EaseOut_Step: integer; begin EaseOut_Step := aPlayerScoreScreenRatings[PlayerNumber].RateEaseStep; MaxVal := Theme.Score.StaticRatings[PlayerNumber + ArrayStartModifier].W; RaiseStep := EaseOut_Step; - if (MaxVal > 0) AND (RaiseStep > 0) then + if (MaxVal > 0) and (RaiseStep > 0) then RaiseStep := RaiseStep / MaxVal; - if (RaiseStep = 1) then - begin - ReturnValue := MaxVal; - end - else - begin - p := MaxVal * 0.4; + if (RaiseStep = 1) then + begin + ReturnValue := MaxVal; + end + else + begin + p := MaxVal * 0.4; - s := p/(2*PI) * arcsin (1); - ReturnValue := MaxVal * power(2,-5 * RaiseStep) * sin( (RaiseStep * MaxVal - s) * (2 * PI) / p) + MaxVal; + s := p/(2*PI) * arcsin (1); + ReturnValue := MaxVal * power(2,-5 * RaiseStep) * sin( (RaiseStep * MaxVal - s) * (2 * PI) / p) + MaxVal; - inc(aPlayerScoreScreenRatings[PlayerNumber].RateEaseStep); - aPlayerScoreScreenRatings[PlayerNumber].RateEaseValue := ReturnValue; - end; + inc(aPlayerScoreScreenRatings[PlayerNumber].RateEaseStep); + aPlayerScoreScreenRatings[PlayerNumber].RateEaseValue := ReturnValue; + end; Result := ReturnValue; end; - -procedure TscreenScore.EaseBarIn(PlayerNumber : Integer; BarType: String); +procedure TscreenScore.EaseBarIn(PlayerNumber: integer; BarType: string); const - RaiseSmoothness : integer = 100; + RaiseSmoothness: integer = 100; var - MaxHeight : real; - NewHeight : real; + MaxHeight: real; + NewHeight: real; - Height2Reach : real; - RaiseStep : real; - BarStartPosY : single; + Height2Reach: real; + RaiseStep: real; + BarStartPosY: single; - lTmp : real; - Score : integer; + lTmp: real; + Score: integer; begin MaxHeight := Theme.Score.StaticBackLevel[PlayerNumber + ArrayStartModifier].H; @@ -716,10 +768,10 @@ begin aPlayerScoreScreenDatas[PlayerNumber].BarGolden_ActualHeight := NewHeight; end; -procedure TscreenScore.DrawBar(BarType:string; PlayerNumber: integer; BarStartPosY: single; NewHeight: real); +procedure TscreenScore.DrawBar(BarType: string; PlayerNumber: integer; BarStartPosY: single; NewHeight: real); var - Width:real; - BarStartPosX:real; + Width: real; + BarStartPosX: real; begin // this is solely for better readability of the drawing Width := Theme.Score.StaticBackLevel[PlayerNumber + ArrayStartModifier].W; @@ -773,15 +825,15 @@ begin glDisable(GL_TEXTURE_2d); end; -procedure TScreenScore.EaseScoreIn(PlayerNumber: integer; ScoreType : String); +procedure TScreenScore.EaseScoreIn(PlayerNumber: integer; ScoreType: string); const - RaiseSmoothness : integer = 100; + RaiseSmoothness: integer = 100; var - RaiseStep : Real; - lTmpA : Real; - ScoreReached :Integer; - EaseOut_Step :Real; - ActualScoreValue:integer; + RaiseStep: real; + lTmpA: real; + ScoreReached: integer; + EaseOut_Step: real; + ActualScoreValue: integer; begin if (ScoreType = 'Note') then begin @@ -813,7 +865,7 @@ begin // quadratic easing out - decelerating to zero velocity // -end_position * current_time * ( current_time - 2 ) + start_postion lTmpA := (-ScoreReached * RaiseStep * (RaiseStep - 20)); - if ( lTmpA > 0 ) AND + if ( lTmpA > 0 ) and ( RaiseSmoothness > 0 ) then begin if (ScoreType = 'Note') then @@ -867,7 +919,6 @@ begin Text[TextGoldenNotesScore[Item]].Text := S; //end of fix - end; end. diff --git a/Lua/src/screens/UScreenSing.pas b/Lua/src/screens/UScreenSing.pas index cbd30a8e..f907051c 100644 --- a/Lua/src/screens/UScreenSing.pas +++ b/Lua/src/screens/UScreenSing.pas @@ -33,23 +33,24 @@ interface {$I switches.inc} - -uses UMenu, - UMusic, - SDL, +uses SysUtils, + SDL, + TextGL, + gl, UFiles, - UTime, - USongs, + UGraphicClasses, UIni, ULog, - UTexture, ULyrics, - TextGL, - gl, - UThemes, - UGraphicClasses, + UMenu, + UMusic, USingScores, + USongs, + UTexture, + UThemes, + UPath, + UTime, UHookableEvent; type @@ -59,26 +60,26 @@ type type TScreenSing = class(TMenu) - private + protected VideoLoaded: boolean; eSongLoaded: THookableEvent; //< event is called after lyrics of a song are loaded on OnShow protected - Paused: boolean; //Pause Mod + Paused: boolean; //pause Mod LyricsSync: TLyricsSyncSource; NumEmptySentences: integer; public - // TimeBar fields + // timebar fields StaticTimeProgress: integer; TextTimeText: integer; StaticP1: integer; TextP1: integer; - //shown when game is in 2/4 player modus + // shown when game is in 2/4 player modus StaticP1TwoP: integer; TextP1TwoP: integer; - //shown when game is in 3/6 player modus + // shown when game is in 3/6 player modus StaticP1ThreeP: integer; TextP1ThreeP: integer; @@ -97,9 +98,12 @@ type FadeOut: boolean; Lyrics: TLyricEngine; - //Score Manager: + // score manager: Scores: TSingScores; + //the song was sung to the end + SungToEnd: boolean; + fShowVisualization: boolean; fCurrentVideoPlaybackEngine: IVideoPlayback; @@ -117,57 +121,60 @@ type procedure EndSong; constructor Create; override; - procedure onShow; override; - procedure onShowFinish; override; - procedure onHide; override; - - function ParseInput(PressedKey: cardinal; CharCode: widechar; + procedure OnShow; override; + procedure OnShowFinish; override; + procedure OnHide; override; + + function ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; override; function Draw: boolean; override; procedure Finish; virtual; - procedure Pause; // Toggle Pause + procedure Pause; // toggle pause - procedure OnSentenceEnd(SentenceIndex: cardinal); // for LineBonus + Singbar - procedure OnSentenceChange(SentenceIndex: cardinal); // for Golden Notes + procedure OnSentenceEnd(SentenceIndex: cardinal); // for linebonus + singbar + procedure OnSentenceChange(SentenceIndex: cardinal); // for golden notes end; implementation -uses UGraphic, - UDraw, - UMain, - USong, +uses Classes, - URecord, + Math, + UDraw, + UGraphic, ULanguage, + UNote, + URecord, + USong, UDisplay, UParty, - Math; + UUnicodeUtils; + +// method for input parsing. if false is returned, getnextwindow +// should be checked to know the next window to load; - // Method for input parsing. If False is returned, GetNextWindow - // should be checked to know the next window to load; -function TScreenSing.ParseInput(PressedKey: cardinal; CharCode: widechar; +function TScreenSing.ParseInput(PressedKey: Cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; begin - Result := True; + Result := true; if (PressedDown) then - begin // Key Down - // check normal keys - case WideCharUpperCase(CharCode)[1] of - 'Q': + begin // key down + // check normal keys + case UCS4UpperCase(CharCode) of + Ord('Q'): begin - //When not ask before Exit then Finish now + // when not ask before exit then finish now if (Ini.AskbeforeDel <> 1) then Finish - //else just Pause and let the Popup make the Work + // else just pause and let the popup make the work else if not Paused then Pause; - Result := False; + Result := false; Exit; end; - 'V': //Show Visualization + Ord('V'): // show visualization begin fShowVisualization := not fShowVisualization; @@ -181,7 +188,7 @@ begin Exit; end; - 'P': + Ord('P'): begin Pause; Exit; @@ -193,7 +200,7 @@ begin SDLK_ESCAPE, SDLK_BACKSPACE: begin - //Record Sound Hack: + // record sound hack: //Sound[0].BufferLong Finish; @@ -206,7 +213,7 @@ begin Pause; end; - SDLK_TAB: //Change Visualization Preset + SDLK_TAB: // change visualization preset begin if fShowVisualization then fCurrentVideoPlaybackEngine.Position := now; // move to a random position @@ -216,8 +223,8 @@ begin begin end; - // Up and Down could be done at the same time, - // but I don't want to declare variables inside + // up and down could be done at the same time, + // but i don't want to declare variables inside // functions like this one, called so many times SDLK_DOWN: begin @@ -229,57 +236,62 @@ begin end; end; -//Pause Mod +// pause mod procedure TScreenSing.Pause; +var + VideoFile: IPath; begin - if (not Paused) then //enable Pause + if (not Paused) then // enable pause begin - // pause Time - Paused := True; + // pause time + Paused := true; LyricsState.Pause(); - // pause Music + // pause music AudioPlayback.Pause; - // pause Video - if (CurrentSong.Video <> '') and FileExists(CurrentSong.Path + - CurrentSong.Video) then + // pause video + VideoFile := CurrentSong.Path.Append(CurrentSong.Video); + if (CurrentSong.Video.IsSet) and VideoFile.Exists then fCurrentVideoPlaybackEngine.Pause; end - else //disable Pause + else // disable pause begin LyricsState.Resume(); - // Play Music + // play music AudioPlayback.Play; - // Video - if (CurrentSong.Video <> '') and FileExists(CurrentSong.Path + - CurrentSong.Video) then + // video + VideoFile := CurrentSong.Path.Append(CurrentSong.Video); + if (CurrentSong.Video.IsSet) and VideoFile.Exists then fCurrentVideoPlaybackEngine.Pause; - Paused := False; + Paused := false; end; end; -//Pause Mod End +// pause mod end constructor TScreenSing.Create; begin inherited Create; - fShowVisualization := False; + //too dangerous, a mouse button is quickly pressed by accident + RightMbESC := false; + + fShowVisualization := false; fCurrentVideoPlaybackEngine := VideoPlayback; - //Create Score Class + // create score class Scores := TSingScores.Create; Scores.LoadfromTheme; LoadFromTheme(Theme.Sing); - //TimeBar + // timebar StaticTimeProgress := AddStatic(Theme.Sing.StaticTimeProgress); TextTimeText := AddText(Theme.Sing.TextTimeText); @@ -291,7 +303,7 @@ begin StaticP1TwoP := AddStatic(Theme.Sing.StaticP1TwoP); TextP1TwoP := AddText(Theme.Sing.TextP1TwoP); - // | P2 + // | P2 StaticP2R := AddStatic(Theme.Sing.StaticP2R); TextP2R := AddText(Theme.Sing.TextP2R); @@ -299,22 +311,22 @@ begin StaticP1ThreeP := AddStatic(Theme.Sing.StaticP1ThreeP); TextP1ThreeP := AddText(Theme.Sing.TextP1ThreeP); - // | P2 + // | P2 StaticP2M := AddStatic(Theme.Sing.StaticP2M); TextP2M := AddText(Theme.Sing.TextP2M); - // | P3 + // | P3 StaticP3R := AddStatic(Theme.Sing.StaticP3R); TextP3R := AddText(Theme.Sing.TextP3R); StaticPausePopup := AddStatic(Theme.Sing.PausePopUp); - //<note>Pausepopup is not visibile at the beginning</note> - Static[StaticPausePopup].Visible := False; + // <note> pausepopup is not visibile at the beginning </note> + Static[StaticPausePopup].Visible := false; Lyrics := TLyricEngine.Create( - Skin_LyricsUpperX, Skin_LyricsUpperY, Skin_LyricsUpperW, Skin_LyricsUpperH, - Skin_LyricsLowerX, Skin_LyricsLowerY, Skin_LyricsLowerW, Skin_LyricsLowerH); + Theme.LyricBar.UpperX, Theme.LyricBar.UpperY, Theme.LyricBar.UpperW, Theme.LyricBar.UpperH, + Theme.LyricBar.LowerX, Theme.LyricBar.LowerY, Theme.LyricBar.LowerW, Theme.LyricBar.LowerH); LyricsSync := TLyricsSyncSource.Create(); @@ -323,24 +335,26 @@ begin ClearSettings; end; -procedure TScreenSing.onShow; +procedure TScreenSing.OnShow; var - P: integer; + Index: integer; V1: boolean; - V1TwoP: boolean; //Position of ScoreBox in two-player mode - V1ThreeP: boolean; //Position of ScoreBox in three-player mode + V1TwoP: boolean; // position of score box in two player mode + V1ThreeP: boolean; // position of score box in three player mode V2R: boolean; V2M: boolean; V3R: boolean; Color: TRGB; - + VideoFile, BgFile: IPath; success: boolean; begin inherited; - Log.LogStatus('Begin', 'onShow'); - FadeOut := False; + Log.LogStatus('Begin', 'OnShow'); + FadeOut := false; + //the song was sung to the end + SungToEnd := false; ClearSettings; Party.CallBeforeSing; @@ -354,12 +368,12 @@ begin Color.B := 0; // dummy atm <- \(O.o)/? B like bummy? // add new players - for P := 0 to PlayersPlay - 1 do + for Index := 0 to PlayersPlay - 1 do begin - Scores.AddPlayer(Tex_ScoreBG[P], Color); + Scores.AddPlayer(Tex_ScoreBG[Index], Color); end; - Scores.Init; //Get Positions for Players + Scores.Init; // get positions for players // prepare players SetLength(Player, PlayersPlay); @@ -367,92 +381,87 @@ begin case PlayersPlay of 1: begin - V1 := True; - V1TwoP := False; - V1ThreeP := False; - V2R := False; - V2M := False; - V3R := False; + V1 := true; + V1TwoP := false; + V1ThreeP := false; + V2R := false; + V2M := false; + V3R := false; end; 2: begin - V1 := False; - V1TwoP := True; - V1ThreeP := False; - V2R := True; - V2M := False; - V3R := False; + V1 := false; + V1TwoP := true; + V1ThreeP := false; + V2R := true; + V2M := false; + V3R := false; end; 3: begin - V1 := False; - V1TwoP := False; - V1ThreeP := True; - V2R := False; - V2M := True; - V3R := True; + V1 := false; + V1TwoP := false; + V1ThreeP := true; + V2R := false; + V2M := true; + V3R := true; end; 4: begin // double screen - V1 := False; - V1TwoP := True; - V1ThreeP := False; - V2R := True; - V2M := False; - V3R := False; + V1 := false; + V1TwoP := true; + V1ThreeP := false; + V2R := true; + V2M := false; + V3R := false; end; 6: begin // double screen - V1 := False; - V1TwoP := False; - V1ThreeP := True; - V2R := False; - V2M := True; - V3R := True; + V1 := false; + V1TwoP := false; + V1ThreeP := true; + V2R := false; + V2M := true; + V3R := true; end; end; - //This one is shown in 1P mode + // this one is shown in 1P mode Static[StaticP1].Visible := V1; Text[TextP1].Visible := V1; - - //This one is shown in 2/4P mode + // this one is shown in 2/4P mode Static[StaticP1TwoP].Visible := V1TwoP; Text[TextP1TwoP].Visible := V1TwoP; Static[StaticP2R].Visible := V2R; Text[TextP2R].Visible := V2R; - - //This one is shown in 3/6P mode + // this one is shown in 3/6P mode Static[StaticP1ThreeP].Visible := V1ThreeP; Text[TextP1ThreeP].Visible := V1ThreeP; - Static[StaticP2M].Visible := V2M; Text[TextP2M].Visible := V2M; - Static[StaticP3R].Visible := V3R; Text[TextP3R].Visible := V3R; - - // FIXME: sets Path and Filename to '' + // FIXME: sets path and filename to '' ResetSingTemp; CurrentSong := CatSongs.Song[CatSongs.Selected]; - // FIXME: bad style, put the try-except into LoadSong() and not here + // FIXME: bad style, put the try-except into loadsong() and not here try - // Check if file is XML - if copy(CurrentSong.FileName, length(CurrentSong.FileName) - 3, 4) = '.xml' then + // check if file is xml + if CurrentSong.FileName.GetExtension.ToUTF8 = '.xml' then success := CurrentSong.LoadXMLSong() else success := CurrentSong.LoadSong(); except - success := False; + success := false; end; if (not success) then @@ -471,7 +480,7 @@ begin Exit; end; - // reset video playback engine, to play video clip... + // reset video playback engine, to play video clip ... fCurrentVideoPlaybackEngine.Close; fCurrentVideoPlaybackEngine := VideoPlayback; @@ -481,40 +490,43 @@ begin * + Blank : Nothing has been set, this is our fallback * + Picture : Picture has been set, and exists - otherwise we fallback * + Video : Video has been set, and exists - otherwise we fallback - * + Visualization: + Off : No Visialization - * + WhenNoVideo: Overwrites Blank and Picture - * + On : Overwrites Blank, Picture and Video + * + Visualization: + Off : No visualization + * + WhenNoVideo: Overwrites blank and picture + * + On : Overwrites blank, picture and video *} {* * set background to: video *} - VideoLoaded := False; - fShowVisualization := False; - if (CurrentSong.Video <> '') and FileExists(CurrentSong.Path + CurrentSong.Video) then + VideoLoaded := false; + fShowVisualization := false; + VideoFile := CurrentSong.Path.Append(CurrentSong.Video); + if (CurrentSong.Video.IsSet) and VideoFile.IsFile then begin - if (fCurrentVideoPlaybackEngine.Open(CurrentSong.Path + CurrentSong.Video)) then + if (fCurrentVideoPlaybackEngine.Open(VideoFile)) then begin - fShowVisualization := False; + fShowVisualization := false; fCurrentVideoPlaybackEngine := VideoPlayback; fCurrentVideoPlaybackEngine.Position := CurrentSong.VideoGAP + CurrentSong.Start; fCurrentVideoPlaybackEngine.Play; - VideoLoaded := True; + VideoLoaded := true; end; end; {* * set background to: picture *} - if (CurrentSong.Background <> '') and (VideoLoaded = False) + if (CurrentSong.Background.IsSet) and (VideoLoaded = false) and (TVisualizerOption(Ini.VisualizerOption) = voOff) then + begin + BgFile := CurrentSong.Path.Append(CurrentSong.Background); try - Tex_Background := Texture.LoadTexture(CurrentSong.Path + CurrentSong.Background); + Tex_Background := Texture.LoadTexture(BgFile); except - Log.LogError('Background could not be loaded: ' + CurrentSong.Path + - CurrentSong.Background); + Log.LogError('Background could not be loaded: ' + BgFile.ToNative); Tex_Background.TexNum := 0; end + end else begin Tex_Background.TexNum := 0; @@ -525,7 +537,7 @@ begin *} if (TVisualizerOption(Ini.VisualizerOption) in [voOn]) then begin - fShowVisualization := True; + fShowVisualization := true; fCurrentVideoPlaybackEngine := Visualization; if (fCurrentVideoPlaybackEngine <> nil) then fCurrentVideoPlaybackEngine.Play; @@ -535,9 +547,9 @@ begin * set background to: visualization (Videos are still shown) *} if ((TVisualizerOption(Ini.VisualizerOption) in [voWhenNoVideo]) and - (VideoLoaded = False)) then + (VideoLoaded = false)) then begin - fShowVisualization := True; + fShowVisualization := true; fCurrentVideoPlaybackEngine := Visualization; if (fCurrentVideoPlaybackEngine <> nil) then fCurrentVideoPlaybackEngine.Play; @@ -562,8 +574,24 @@ begin // prepare and start voice-capture AudioInput.CaptureStart; - for P := 0 to High(Player) do - ClearScores(P); + // clear the scores of all players + + for Index := 0 to High(Player) do + with Player[Index] do + begin + Score := 0; + ScoreLine := 0; + ScoreGolden := 0; + + ScoreInt := 0; + ScoreLineInt := 0; + ScoreGoldenInt := 0; + ScoreTotalInt := 0; + + ScoreLast := 0; + + LastSentencePerfect := false; + end; // main text Lyrics.Clear(CurrentSong.BPM[0].BPM, CurrentSong.Resolution); @@ -610,33 +638,36 @@ begin end; end; // case - // Initialize lyrics by filling its queue + // initialize lyrics by filling its queue while (not Lyrics.IsQueueFull) and (Lyrics.LineCounter <= High(Lines[0].Line)) do begin Lyrics.AddLine(@Lines[0].Line[Lyrics.LineCounter]); end; - // Deactivate pause - Paused := False; + // deactivate pause + Paused := false; - // Kill all stars not killed yet (GoldenStarsTwinkle Mod) + // kill all stars not killed yet (goldenstarstwinkle mod) GoldenRec.SentenceChange; - // set Position of Line Bonus - Line Bonus end - // set number of empty sentences for Line Bonus + // set position of line bonus - line bonus end + // set number of empty sentences for line bonus NumEmptySentences := 0; - for P := Low(Lines[0].Line) to High(Lines[0].Line) do - if Lines[0].Line[P].TotalNotes = 0 then + for Index := Low(Lines[0].Line) to High(Lines[0].Line) do + if Lines[0].Line[Index].TotalNotes = 0 then Inc(NumEmptySentences); eSongLoaded.CallHookChain(False); - Log.LogStatus('End', 'onShow'); + Log.LogStatus('End', 'OnShow'); end; procedure TScreenSing.onShowFinish; begin + // hide cursor on singscreen show + Display.SetCursor; + // start lyrics LyricsState.Resume(); @@ -666,9 +697,9 @@ begin Settings.Finish := True; end; -procedure TScreenSing.onHide; +procedure TScreenSing.OnHide; begin - // Unload background texture + // background texture if (Tex_Background.TexNum > 0) then begin glDeleteTextures(1, PGLuint(@Tex_Background.TexNum)); @@ -676,6 +707,7 @@ begin end; Background.OnFinish; + Display.SetCursor; end; function TScreenSing.Draw: boolean; @@ -684,11 +716,18 @@ var Sec: integer; T: integer; CurLyricsTime: real; + Line: TLyricLine; + LastWord: TLyricWord; begin - Background.Draw; - // set player names (for 2 screens and only Singstar skin) + // draw background picture (if any, and if no visualizations) + // when we don't check for visualizations the visualizations would + // be overdrawn by the picture when {UNDEFINED UseTexture} in UVisualizer + if (not fShowVisualization) then + SingDrawBackground; + + // set player names (for 2 screens and only singstar skin) if ScreenAct = 1 then begin Text[TextP1].Text := 'P1'; @@ -716,7 +755,6 @@ begin end; // case end; // if - //// // dual screen, part 1 //////////////////////// @@ -725,29 +763,26 @@ begin // will move the statics and texts to the correct screen here. // FIXME: clean up this weird stuff. Commenting this stuff out, nothing // was missing on screen w/ 6 players - so do we even need this stuff? - Static[StaticP1].Texture.X := Static[StaticP1].Texture.X + 10 * ScreenX; + {Static[StaticP1].Texture.X := Static[StaticP1].Texture.X + 10 * ScreenX; - Text[TextP1].X := Text[TextP1].X + 10 * ScreenX; + Text[TextP1].X := Text[TextP1].X + 10 * ScreenX; } {Static[StaticP1ScoreBG].Texture.X := Static[StaticP1ScoreBG].Texture.X + 10*ScreenX; Text[TextP1Score].X := Text[TextP1Score].X + 10*ScreenX;} + {Static[StaticP2R].Texture.X := Static[StaticP2R].Texture.X + 10 * ScreenX; - Static[StaticP2R].Texture.X := Static[StaticP2R].Texture.X + 10 * ScreenX; - - Text[TextP2R].X := Text[TextP2R].X + 10 * ScreenX; + Text[TextP2R].X := Text[TextP2R].X + 10 * ScreenX; } {Static[StaticP2RScoreBG].Texture.X := Static[StaticP2RScoreBG].Texture.X + 10*ScreenX; Text[TextP2RScore].X := Text[TextP2RScore].X + 10*ScreenX;} // end of weird stuff + { + Static[1].Texture.X := Static[1].Texture.X + 10 * ScreenX; } - Static[1].Texture.X := Static[1].Texture.X + 10 * ScreenX; - - for T := 0 to 1 do - Text[T].X := Text[T].X + 10 * ScreenX; - - + { for T := 0 to 1 do + Text[T].X := Text[T].X + 10 * ScreenX; } // retrieve current lyrics time, we have to store the value to avoid // that min- and sec-values do not match @@ -768,15 +803,25 @@ begin // Note: there is no menu and the animated background brakes the video playback //DrawBG; - // Draw Background - SingDrawBackground; + //the song was sung to the end? + Line := Lyrics.GetUpperLine(); + if Line.LastLine then + begin + LastWord := Line.Words[Length(Line.Words)-1]; + if CurLyricsTime >= GetTimeFromBeat(LastWord.Start+LastWord.Length) then + SungToEnd := true; + end; // update and draw movie if (ShowFinish and (VideoLoaded or fShowVisualization)) then begin if assigned(fCurrentVideoPlaybackEngine) then begin - fCurrentVideoPlaybackEngine.GetFrame(CurrentSong.VideoGAP + LyricsState.GetCurrentTime()); + // Just call this once + // when Screens = 2 + if (ScreenAct = 1) then + fCurrentVideoPlaybackEngine.GetFrame(CurrentSong.VideoGAP + LyricsState.GetCurrentTime()); + fCurrentVideoPlaybackEngine.DrawGL(ScreenAct); end; end; @@ -803,7 +848,7 @@ begin if (not FadeOut) then begin Finish; - FadeOut := True; + FadeOut := true; end; end; end; @@ -811,10 +856,10 @@ begin // always draw custom items SingDraw; - //GoldenNoteStarsTwinkle + // goldennotestarstwinkle GoldenRec.SpawnRec; - //Draw Scores + // draw scores Scores.Draw; //// @@ -825,30 +870,30 @@ begin // will move the statics and texts to the correct screen here. // FIXME: clean up this weird stuff - Static[StaticP1].Texture.X := Static[StaticP1].Texture.X - 10 * ScreenX; + {Static[StaticP1].Texture.X := Static[StaticP1].Texture.X - 10 * ScreenX; Text[TextP1].X := Text[TextP1].X - 10 * ScreenX; Static[StaticP2R].Texture.X := Static[StaticP2R].Texture.X - 10 * ScreenX; Text[TextP2R].X := Text[TextP2R].X - 10 * ScreenX; - //end of weird + // end of weird Static[1].Texture.X := Static[1].Texture.X - 10 * ScreenX; for T := 0 to 1 do - Text[T].X := Text[T].X - 10 * ScreenX; + Text[T].X := Text[T].X - 10 * ScreenX; } - // Draw Pausepopup - // FIXME: this is a workaround that the Static is drawn over the Lyrics, Lines, Scores and Effects + // draw pausepopup + // FIXME: this is a workaround that the static is drawn over the lyrics, lines, scores and effects // maybe someone could find a better solution if Paused then begin - Static[StaticPausePopup].Visible := True; + Static[StaticPausePopup].Visible := true; Static[StaticPausePopup].Draw; - Static[StaticPausePopup].Visible := False; + Static[StaticPausePopup].Visible := false; end; - Result := True; + Result := true; end; procedure TScreenSing.Finish; @@ -864,9 +909,9 @@ begin Visualization.Close; // to prevent drawing closed video - VideoLoaded := False; + VideoLoaded := false; - //Kill all Stars and Effects + // kill all stars and effects GoldenRec.KillAll; if (Ini.SavePlayback = 1) then @@ -879,14 +924,14 @@ begin Log.LogBenchmark('Creating files', 0); end; - SetFontItalic(False); + SetFontItalic(false); Party.CallAfterSing; end; procedure TScreenSing.OnSentenceEnd(SentenceIndex: cardinal); var - PlayerIndex: integer; + PlayerIndex: byte; CurrentPlayer: PPLayer; CurrentScore: real; Line: PLine; @@ -920,15 +965,19 @@ begin CurrentPlayer := @Player[PlayerIndex]; CurrentScore := CurrentPlayer.Score + CurrentPlayer.ScoreGolden; - // Line Bonus + // line bonus // points for this line LineScore := CurrentScore - CurrentPlayer.ScoreLast; - // determine LinePerfection - // Note: the "+2" extra points are a little bonus so the player does not - // have to be that perfect to reach the bonus steps. - LinePerfection := (LineScore + 2) / MaxLineScore; + // check for lines with low points + if (MaxLineScore <= 2) then + LinePerfection := 1 + else + // determine LinePerfection + // Note: the "+2" extra points are a little bonus so the player does not + // have to be that perfect to reach the bonus steps. + LinePerfection := LineScore / (MaxLineScore - 2); // clamp LinePerfection to range [0..1] if (LinePerfection < 0) then @@ -945,7 +994,7 @@ begin // apply line-bonus CurrentPlayer.ScoreLine := CurrentPlayer.ScoreLine + LineBonus * LinePerfection; - CurrentPlayer.ScoreLineInt := Round(CurrentPlayer.ScoreLine / 10) * 10; + CurrentPlayer.ScoreLineInt := Floor(CurrentPlayer.ScoreLine / 10) * 10; // update total score CurrentPlayer.ScoreTotalInt := CurrentPlayer.ScoreInt + @@ -957,7 +1006,7 @@ begin Scores.SpawnPopUp(PlayerIndex, Rating, CurrentPlayer.ScoreTotalInt); end; - // PerfectLineTwinkle (effect), Part 1 + // PerfectLineTwinkle (effect), part 1 if (Ini.EffectSing = 1) then CurrentPlayer.LastSentencePerfect := (LinePerfection >= 1); @@ -965,7 +1014,7 @@ begin CurrentPlayer.ScoreLast := CurrentScore; end; - // PerfectLineTwinkle (effect), Part 2 + // PerfectLineTwinkle (effect), part 2 if (Ini.EffectSing = 1) then GoldenRec.SpawnPerfectLineTwinkle; end; @@ -974,14 +1023,14 @@ end; // SentenceIndex: index of the new active sentence procedure TScreenSing.OnSentenceChange(SentenceIndex: cardinal); begin - //GoldenStarsTwinkle + // goldenstarstwinkle GoldenRec.SentenceChange; - // Fill lyrics queue and set upper line to the current sentence + // fill lyrics queue and set upper line to the current sentence while (Lyrics.GetUpperLineIndex() < SentenceIndex) or (not Lyrics.IsQueueFull) do begin - // Add the next line to the queue or a dummy if no more lines are available + // add the next line to the queue or a dummy if no more lines are available if (Lyrics.LineCounter <= High(Lines[0].Line)) then Lyrics.AddLine(@Lines[0].Line[Lyrics.LineCounter]) else diff --git a/Lua/src/screens/UScreenSingModi.pas b/Lua/src/screens/UScreenSingModi.pas index 892bfe6a..48d1e9a1 100644 --- a/Lua/src/screens/UScreenSingModi.pas +++ b/Lua/src/screens/UScreenSingModi.pas @@ -33,74 +33,45 @@ interface {$I switches.inc} - -uses UMenu, - UMusic, - SDL, - SysUtils, - UFiles, - UTime, - USongs, - UIni, - ULog, - UTexture, - ULyrics, - TextGL, - gl, - - UThemes, - UScreenSing, - ModiSDK; +uses + UMenu, + UMusic, + SDL, + SysUtils, + UFiles, + UTime, + USongs, + UIni, + ULog, + UTexture, + ULyrics, + TextGL, + gl, + UPath, + UThemes, + UScreenSing, + ModiSDK; type TScreenSingModi = class(TScreenSing) protected - //paused: boolean; //Pause Mod - //PauseTime: Real; - //NumEmptySentences: integer; + public - //TextTime: integer; - - //StaticP1: integer; - //StaticP1ScoreBG: integer; - //TextP1: integer; - //TextP1Score: integer; - - //StaticP2R: integer; - //StaticP2RScoreBG: integer; - //TextP2R: integer; - //TextP2RScore: integer; - - //StaticP2M: integer; - //StaticP2MScoreBG: integer; - //TextP2M: integer; - //TextP2MScore: integer; - - //StaticP3R: integer; - //StaticP3RScoreBG: integer; - //TextP3R: integer; - //TextP3RScore: integer; - - //Tex_Background: TTexture; - //FadeOut: boolean; - //LyricMain: TLyric; - //LyricSub: TLyric; - Winner: Byte; //Who Wins + Winner: byte; //Who Wins PlayerInfo: TPlayerInfo; TeamInfo: TTeamInfo; constructor Create; override; - procedure onShow; override; + procedure OnShow; override; //procedure onShowFinish; override; - function ParseInput(PressedKey: Cardinal; CharCode: WideChar; PressedDown: Boolean): Boolean; override; + function ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; override; function Draw: boolean; override; procedure Finish; override; - //procedure Pause; //Pause Mod(Toggles Pause) end; type TCustomSoundEntry = record - Filename : String; + Filename : IPath; Stream : TAudioPlaybackStream; end; @@ -109,24 +80,44 @@ var CustomSounds: array of TCustomSoundEntry; //Procedured for Plugin -function LoadTex (const Name: PChar; Typ: TTextureType): TsmallTexture; stdcall; -//function Translate (const Name: PChar): PChar; stdcall; -procedure Print (const Style, Size: Byte; const X, Y: Real; const Text: PChar); stdcall; //Procedure to Print Text -function LoadSound (const Name: PChar): Cardinal; stdcall; //Procedure that loads a Custom Sound -procedure PlaySound (const Index: Cardinal); stdcall; //Plays a Custom Sound +function LoadTex(const Name: PChar; Typ: TTextureType): TsmallTexture; + {$IFDEF MSWINDOWS} stdcall; {$ELSE} cdecl; {$ENDIF} +//function Translate (const Name: PChar): PChar; +// {$IFDEF MSWINDOWS} stdcall; {$ELSE} cdecl; {$ENDIF} +//Procedure to Print Text +procedure Print(const Style, Size: byte; const X, Y: real; const Text: PChar); + {$IFDEF MSWINDOWS} stdcall; {$ELSE} cdecl; {$ENDIF} +//Procedure that loads a Custom Sound +function LoadSound(const Name: PChar): cardinal; + {$IFDEF MSWINDOWS} stdcall; {$ELSE} cdecl; {$ENDIF} +//Plays a Custom Sound +procedure PlaySound(const Index: cardinal); + {$IFDEF MSWINDOWS} stdcall; {$ELSE} cdecl; {$ENDIF} //Utilys function ToSentences(Const Lines: TLines): TSentences; implementation -uses UGraphic, UDraw, UMain, Classes, URecord, ULanguage, math, UDLLManager, USkins, UGraphicClasses; -// Method for input parsing. If False is returned, GetNextWindow +uses + Classes, + Math, + UDLLManager, + UDraw, + UGraphic, + UGraphicClasses, + ULanguage, + UNote, + UPathUtils, + URecord, + USkins; + +// Method for input parsing. If false is returned, GetNextWindow // should be checked to know the next window to load; -function TScreenSingModi.ParseInput(PressedKey: Cardinal; CharCode: WideChar; PressedDown: Boolean): Boolean; +function TScreenSingModi.ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; begin Result := true; - If (PressedDown) Then + if (PressedDown) then begin // Key Down case PressedKey of @@ -152,7 +143,7 @@ end; function ToSentences(Const Lines: TLines): TSentences; var - I, J: Integer; + I, J: integer; begin Result.Current := Lines.Current; Result.High := Lines.High; @@ -167,7 +158,6 @@ begin Result.Sentence[I].Start := Lines.Line[I].Start; Result.Sentence[I].StartNote := Lines.Line[I].Note[0].Start; Result.Sentence[I].Lyric := Lines.Line[I].Lyric; - Result.Sentence[I].LyricWidth := Lines.Line[I].LyricWidth; Result.Sentence[I].End_ := Lines.Line[I].End_; Result.Sentence[I].BaseNote := Lines.Line[I].BaseNote; Result.Sentence[I].HighNote := Lines.Line[I].HighNote; @@ -186,9 +176,9 @@ begin end; end; -procedure TScreenSingModi.onShow; +procedure TScreenSingModi.OnShow; var - I: Integer; + I: integer; begin inherited; @@ -210,14 +200,14 @@ begin PlayerInfo.Playerinfo[I].Name := PChar(Ini.Name[I]); PlayerInfo.Playerinfo[I].Score := 0; PlayerInfo.Playerinfo[I].Bar := 50; - PlayerInfo.Playerinfo[I].Enabled := True; + PlayerInfo.Playerinfo[I].Enabled := true; end; for I := PlayerInfo.NumPlayers to high(PlayerInfo.Playerinfo) do begin PlayerInfo.Playerinfo[I].Score:= 0; PlayerInfo.Playerinfo[I].Bar := 0; - PlayerInfo.Playerinfo[I].Enabled := False; + PlayerInfo.Playerinfo[I].Enabled := false; end; {Case PlayersPlay of @@ -264,18 +254,22 @@ begin end; // Set Background (Little Workaround, maybe change sometime) - if (DLLMan.Selected.LoadBack) AND (DLLMan.Selected.LoadSong) then + if (DLLMan.Selected.LoadBack) and (DLLMan.Selected.LoadSong) then ScreenSing.Tex_Background := Tex_Background; Winner := 0; //Set Score Visibility - {if PlayersPlay = 1 then begin + Scores.Visible := DLLMan.Selected.ShowScore; + + {if PlayersPlay = 1 then + begin Text[TextP1Score].Visible := DLLMan.Selected.ShowScore; Static[StaticP1ScoreBG].Visible := DLLMan.Selected.ShowScore; end; - if (PlayersPlay = 2) OR (PlayersPlay = 4) then begin + if (PlayersPlay = 2) or (PlayersPlay = 4) then + begin Text[TextP1TwoPScore].Visible := DLLMan.Selected.ShowScore; Static[StaticP1TwoPScoreBG].Visible := DLLMan.Selected.ShowScore; @@ -283,7 +277,8 @@ begin Static[StaticP2RScoreBG].Visible := DLLMan.Selected.ShowScore; end; - if (PlayersPlay = 3) OR (PlayersPlay = 6) then begin + if (PlayersPlay = 3) or (PlayersPlay = 6) then + begin Text[TextP1ThreePScore].Visible := DLLMan.Selected.ShowScore; Static[StaticP1ThreePScoreBG].Visible := DLLMan.Selected.ShowScore; @@ -319,116 +314,39 @@ begin end; end; - //Show Score - if DLLMan.Selected.ShowScore then - begin - {//ScoreBG Mod - // set player colors - if PlayersPlay = 4 then begin - if ScreenAct = 1 then begin - LoadColor(Static[StaticP1TwoP].Texture.ColR, Static[StaticP1TwoP].Texture.ColG, - Static[StaticP1TwoP].Texture.ColB, 'P1Dark'); - LoadColor(Static[StaticP2R].Texture.ColR, Static[StaticP2R].Texture.ColG, - Static[StaticP2R].Texture.ColB, 'P2Dark'); - - - - LoadColor(Static[StaticP1TwoPScoreBG].Texture.ColR, Static[StaticP1TwoPScoreBG].Texture.ColG, - Static[StaticP1TwoPScoreBG].Texture.ColB, 'P1Dark'); - LoadColor(Static[StaticP2RScoreBG].Texture.ColR, Static[StaticP2RScoreBG].Texture.ColG, - Static[StaticP2RScoreBG].Texture.ColB, 'P2Dark'); - - - - end; - if ScreenAct = 2 then begin - LoadColor(Static[StaticP1TwoP].Texture.ColR, Static[StaticP1TwoP].Texture.ColG, - Static[StaticP1TwoP].Texture.ColB, 'P3Dark'); - LoadColor(Static[StaticP2R].Texture.ColR, Static[StaticP2R].Texture.ColG, - Static[StaticP2R].Texture.ColB, 'P4Dark'); - - - - LoadColor(Static[StaticP1TwoPScoreBG].Texture.ColR, Static[StaticP1TwoPScoreBG].Texture.ColG, - Static[StaticP1TwoPScoreBG].Texture.ColB, 'P3Dark'); - LoadColor(Static[StaticP2RScoreBG].Texture.ColR, Static[StaticP2RScoreBG].Texture.ColG, - Static[StaticP2RScoreBG].Texture.ColB, 'P4Dark'); - - - - end; - end; - - if PlayersPlay = 6 then begin - if ScreenAct = 1 then begin - LoadColor(Static[StaticP1ThreeP].Texture.ColR, Static[StaticP1ThreeP].Texture.ColG, - Static[StaticP1ThreeP].Texture.ColB, 'P1Dark'); - LoadColor(Static[StaticP2M].Texture.ColR, Static[StaticP2M].Texture.ColG, - Static[StaticP2R].Texture.ColB, 'P2Dark'); - LoadColor(Static[StaticP3R].Texture.ColR, Static[StaticP3R].Texture.ColG, - Static[StaticP3R].Texture.ColB, 'P3Dark'); + Background.Draw; + // draw background picture (if any, and if no visualizations) + // when we don't check for visualizations the visualizations would + // be overdrawn by the picture when {UNDEFINED UseTexture} in UVisualizer + if (DllMan.Selected.LoadSong) and (DllMan.Selected.LoadBack) and (not fShowVisualization) then + SingDrawBackground; + // set player names (for 2 screens and only Singstar skin) + if ScreenAct = 1 then + begin + Text[TextP1].Text := 'P1'; + Text[TextP1TwoP].Text := 'P1'; // added for ps3 skin + Text[TextP1ThreeP].Text := 'P1'; // added for ps3 skin + Text[TextP2R].Text := 'P2'; + Text[TextP2M].Text := 'P2'; + Text[TextP3R].Text := 'P3'; + end - LoadColor(Static[StaticP1ThreePScoreBG].Texture.ColR, Static[StaticP1ThreePScoreBG].Texture.ColG, - Static[StaticP1ThreePScoreBG].Texture.ColB, 'P1Dark'); - LoadColor(Static[StaticP2MScoreBG].Texture.ColR, Static[StaticP2MScoreBG].Texture.ColG, - Static[StaticP2RScoreBG].Texture.ColB, 'P2Dark'); - LoadColor(Static[StaticP3RScoreBG].Texture.ColR, Static[StaticP3RScoreBG].Texture.ColG, - Static[StaticP3RScoreBG].Texture.ColB, 'P3Dark'); - - - - end; - if ScreenAct = 2 then begin - LoadColor(Static[StaticP1ThreeP].Texture.ColR, Static[StaticP1ThreeP].Texture.ColG, - Static[StaticP1ThreeP].Texture.ColB, 'P4Dark'); - LoadColor(Static[StaticP2M].Texture.ColR, Static[StaticP2M].Texture.ColG, - Static[StaticP2R].Texture.ColB, 'P5Dark'); - LoadColor(Static[StaticP3R].Texture.ColR, Static[StaticP3R].Texture.ColG, - Static[StaticP3R].Texture.ColB, 'P6Dark'); - - - - - LoadColor(Static[StaticP1ThreePScoreBG].Texture.ColR, Static[StaticP1ThreePScoreBG].Texture.ColG, - Static[StaticP1ThreePScoreBG].Texture.ColB, 'P4Dark'); - LoadColor(Static[StaticP2MScoreBG].Texture.ColR, Static[StaticP2MScoreBG].Texture.ColG, - Static[StaticP2RScoreBG].Texture.ColB, 'P5Dark'); - LoadColor(Static[StaticP3RScoreBG].Texture.ColR, Static[StaticP3RScoreBG].Texture.ColG, - Static[StaticP3RScoreBG].Texture.ColB, 'P6Dark'); - - - - - end; - end; - //end ScoreBG Mod } - - // set player names (for 2 screens and only Singstar skin) - if ScreenAct = 1 then begin - Text[TextP1].Text := 'P1'; - Text[TextP1TwoP].Text := 'P1'; // added for ps3 skin - Text[TextP1ThreeP].Text := 'P1'; // added for ps3 skin - Text[TextP2R].Text := 'P2'; - Text[TextP2M].Text := 'P2'; - Text[TextP3R].Text := 'P3'; - end; - - if ScreenAct = 2 then begin - case PlayersPlay of - 4: begin - Text[TextP1TwoP].Text := 'P3'; - Text[TextP2R].Text := 'P4'; - end; - 6: begin - Text[TextP1ThreeP].Text := 'P4'; - Text[TextP2M].Text := 'P5'; - Text[TextP3R].Text := 'P6'; - end; - end; // case - end; // if - + Else if ScreenAct = 2 then + begin + case PlayersPlay of + 4: begin + Text[TextP1TwoP].Text := 'P3'; + Text[TextP2R].Text := 'P4'; + end; + 6: begin + Text[TextP1ThreeP].Text := 'P4'; + Text[TextP2M].Text := 'P5'; + Text[TextP3R].Text := 'P6'; + end; + end; // case + end; // if // stereo <- and where iss P2M? or P3? Static[StaticP1].Texture.X := Static[StaticP1].Texture.X + 10*ScreenX; @@ -440,92 +358,6 @@ begin Static[StaticP2R].Texture.X := Static[StaticP2R].Texture.X + 10*ScreenX; Text[TextP2R].X := Text[TextP2R].X + 10*ScreenX; - {Static[StaticP2RScoreBG].Texture.X := Static[StaticP2RScoreBG].Texture.X + 10*ScreenX; - Text[TextP2RScore].X := Text[TextP2RScore].X + 10*ScreenX;} - - // .. and scores - {if PlayersPlay = 1 then begin - TextStr := IntToStr(Player[0].ScoreTotalI); - while Length(TextStr) < 5 do TextStr := '0' + TextStr; - Text[TextP1Score].Text := TextStr; - end; - - if PlayersPlay = 2 then begin - TextStr := IntToStr(Player[0].ScoreTotalI); - while Length(TextStr) < 5 do TextStr := '0' + TextStr; - Text[TextP1TwoPScore].Text := TextStr; - - TextStr := IntToStr(Player[1].ScoreTotalI); - while Length(TextStr) < 5 do TextStr := '0' + TextStr; - Text[TextP2RScore].Text := TextStr; - end; - - if PlayersPlay = 3 then begin - TextStr := IntToStr(Player[0].ScoreTotalI); - while Length(TextStr) < 5 do TextStr := '0' + TextStr; - Text[TextP1ThreePScore].Text := TextStr; - - TextStr := IntToStr(Player[1].ScoreTotalI); - while Length(TextStr) < 5 do TextStr := '0' + TextStr; - Text[TextP2MScore].Text := TextStr; - - TextStr := IntToStr(Player[2].ScoreTotalI); - while Length(TextStr) < 5 do TextStr := '0' + TextStr; - Text[TextP3RScore].Text := TextStr; - end; - - if PlayersPlay = 4 then begin - if ScreenAct = 1 then begin - TextStr := IntToStr(Player[0].ScoreTotalI); - while Length(TextStr) < 5 do TextStr := '0' + TextStr; - Text[TextP1TwoPScore].Text := TextStr; - - TextStr := IntToStr(Player[1].ScoreTotalI); - while Length(TextStr) < 5 do TextStr := '0' + TextStr; - Text[TextP2RScore].Text := TextStr; - end; - if ScreenAct = 2 then begin - TextStr := IntToStr(Player[2].ScoreTotalI); - while Length(TextStr) < 5 do TextStr := '0' + TextStr; - Text[TextP1TwoPScore].Text := TextStr; - - TextStr := IntToStr(Player[3].ScoreTotalI); - while Length(TextStr) < 5 do TextStr := '0' + TextStr; - Text[TextP2RScore].Text := TextStr; - end; - end; - - if PlayersPlay = 6 then begin - if ScreenAct = 1 then begin - TextStr := IntToStr(Player[0].ScoreTotalI); - while Length(TextStr) < 5 do TextStr := '0' + TextStr; - Text[TextP1ThreePScore].Text := TextStr; - - TextStr := IntToStr(Player[1].ScoreTotalI); - while Length(TextStr) < 5 do TextStr := '0' + TextStr; - Text[TextP2MScore].Text := TextStr; - - TextStr := IntToStr(Player[2].ScoreTotalI); - while Length(TextStr) < 5 do TextStr := '0' + TextStr; - Text[TextP3RScore].Text := TextStr; - end; - if ScreenAct = 2 then begin - TextStr := IntToStr(Player[3].ScoreTotalI); - while Length(TextStr) < 5 do TextStr := '0' + TextStr; - Text[TextP1ThreePScore].Text := TextStr; - - TextStr := IntToStr(Player[4].ScoreTotalI); - while Length(TextStr) < 5 do TextStr := '0' + TextStr; - Text[TextP2MScore].Text := TextStr; - - TextStr := IntToStr(Player[5].ScoreTotalI); - while Length(TextStr) < 5 do TextStr := '0' + TextStr; - Text[TextP3RScore].Text := TextStr; - end; - end; } - - end; //ShowScore - for S := 1 to 1 do Static[S].Texture.X := Static[S].Texture.X + 10*ScreenX; @@ -546,30 +378,43 @@ begin Text[TextTimeText].Text := Text[TextTimeText].Text + IntToStr(Sec); end; - // draw static menu (BG) - DrawBG; - - //Draw Background - if (DllMan.Selected.LoadSong) AND (DllMan.Selected.LoadBack) then - SingDrawBackground; - // update and draw movie -{ if ShowFinish and CurrentSong.VideoLoaded AND DllMan.Selected.LoadVideo then begin +{ if ShowFinish and CurrentSong.VideoLoaded and DllMan.Selected.LoadVideo then + begin UpdateSmpeg; // this only draws end;} + // update and draw movie + if (ShowFinish and (VideoLoaded or fShowVisualization) and DllMan.Selected.LoadVideo) then + begin + if assigned(fCurrentVideoPlaybackEngine) then + begin + // Just call this once + // when Screens = 2 + if (ScreenAct = 1) then + fCurrentVideoPlaybackEngine.GetFrame(CurrentSong.VideoGAP + LyricsState.GetCurrentTime()); + + fCurrentVideoPlaybackEngine.DrawGL(ScreenAct); + end; + end; + // draw static menu (FG) DrawFG; - if ShowFinish then begin + if ShowFinish then + begin if DllMan.Selected.LoadSong then begin - if (not AudioPlayback.Finished) and ((CurrentSong.Finish = 0) or (LyricsState.GetCurrentTime*1000 <= CurrentSong.Finish)) then begin + if (not AudioPlayback.Finished) and ((CurrentSong.Finish = 0) or (LyricsState.GetCurrentTime*1000 <= CurrentSong.Finish)) then + begin //Pause Mod: if not Paused then Sing(Self); // analyze song - end else begin - if not FadeOut then begin + end + else + begin + if not FadeOut then + begin Finish; FadeOut := true; FadeTo(@ScreenPartyScore); @@ -585,6 +430,9 @@ begin GoldenRec.SpawnRec; //GoldenNoteStarsTwinkle Mod + //Draw Score + Scores.Draw; + //Update PlayerInfo for I := 0 to PlayerInfo.NumPlayers-1 do begin @@ -595,14 +443,15 @@ begin end; end; - if ((ShowFinish) AND (NOT Paused)) then + if ((ShowFinish) and (not Paused)) then begin if not DLLMan.PluginDraw(Playerinfo, Lines[0].Current) then begin - if not FadeOut then begin - Finish; - FadeOut := true; - FadeTo(@ScreenPartyScore); + if not FadeOut then + begin + Finish; + FadeOut := true; + FadeTo(@ScreenPartyScore); end; end; end; @@ -626,14 +475,12 @@ begin {Static[StaticP1ScoreBG].Texture.X := Static[StaticP1ScoreBG].Texture.X - 10*ScreenX; Text[TextP1Score].X := Text[TextP1Score].X - 10*ScreenX;} - Static[StaticP2R].Texture.X := Static[StaticP2R].Texture.X - 10*ScreenX; Text[TextP2R].X := Text[TextP2R].X - 10*ScreenX; {Static[StaticP2RScoreBG].Texture.X := Static[StaticP2RScoreBG].Texture.X - 10*ScreenX; Text[TextP2RScore].X := Text[TextP2RScore].X - 10*ScreenX;} - for S := 1 to 1 do Static[S].Texture.X := Static[S].Texture.X - 10*ScreenX; @@ -654,21 +501,22 @@ Winner := DllMan.PluginFinish(PlayerInfo); //DLLMan.UnLoadPlugin; end; -function LoadTex (const Name: PChar; Typ: TTextureType): TsmallTexture; stdcall; +function LoadTex(const Name: PChar; Typ: TTextureType): TsmallTexture; var - Texname, EXT: String; + TexName: IPath; + Ext: UTF8String; Tex: TTexture; begin //Get texture Name - TexName := Skin.GetTextureFileName(String(Name)); + TexName := Skin.GetTextureFileName(string(Name)); //Get File Typ - Ext := ExtractFileExt(TexName); - if (uppercase(Ext) = '.JPG') then + Ext := TexName.GetExtension().ToUTF8; + if (UpperCase(Ext) = '.JPG') then Ext := 'JPG' else Ext := 'BMP'; - Tex := Texture.LoadTexture(false, PChar(TexName), UTEXTURE.TTextureType(Typ), 0); + Tex := Texture.LoadTexture(false, TexName, UTexture.TTextureType(Typ), 0); Result.TexNum := Tex.TexNum; Result.W := Tex.W; @@ -677,38 +525,41 @@ end; { function Translate (const Name: PChar): PChar; stdcall; begin - Result := PChar(Language.Translate(String(Name))); + Result := PChar(Language.Translate(string(Name))); end; } -procedure Print(const Style, Size: Byte; const X, Y: Real; const Text: PChar); stdcall; //Procedure to Print Text +//Procedure to Print Text +procedure Print(const Style, Size: byte; const X, Y: real; const Text: PChar); begin SetFontItalic ((Style and 128) = 128); SetFontStyle(Style and 7); // FIXME: FONTSIZE // used by Hold_The_Line / TeamDuell - SetFontSize(Size * 3); + SetFontSize(Size); SetFontPos (X, Y); - glPrint (Language.Translate(String(Text))); + glPrint (Language.Translate(string(Text))); end; -function LoadSound(const Name: PChar): Cardinal; stdcall; //Procedure that loads a Custom Sound +//Procedure that loads a Custom Sound +function LoadSound(const Name: PChar): cardinal; var Stream: TAudioPlaybackStream; - i: Integer; - Filename: String; + i: integer; + Filename: IPath; + SoundFile: IPath; begin //Search for Sound in already loaded Sounds - Filename := UpperCase(SoundPath + Name); + SoundFile := SoundPath.Append(Name); for i := 0 to High(CustomSounds) do begin - if (UpperCase(CustomSounds[i].Filename) = Filename) then + if (SoundFile.Equals(CustomSounds[i].Filename, true)) then begin Result := i; Exit; end; end; - Stream := AudioPlayback.OpenSound(SoundPath + String(Name)); + Stream := AudioPlayback.OpenSound(SoundFile); if (Stream = nil) then begin Result := 0; @@ -720,7 +571,8 @@ begin Result := High(CustomSounds); end; -procedure PlaySound(const Index: Cardinal); stdcall; //Plays a Custom Sound +//Plays a Custom Sound +procedure PlaySound(const Index: cardinal); begin if (Index <= High(CustomSounds)) then AudioPlayback.PlaySound(CustomSounds[Index].Stream); diff --git a/Lua/src/screens/UScreenSong.pas b/Lua/src/screens/UScreenSong.pas index 1abf5e01..3f94b6a4 100644 --- a/Lua/src/screens/UScreenSong.pas +++ b/Lua/src/screens/UScreenSong.pas @@ -33,30 +33,33 @@ interface {$I switches.inc} - uses - UMenu, - SDL, - UMusic, - UFiles, - UTime, - UDisplay, - USongs, SysUtils, + SDL, UCommon, - ULog, - UThemes, - UTexture, + UDisplay, + UPath, + UFiles, + UIni, ULanguage, + ULog, + UMenu, + UMenuEqualizer, + UMusic, USong, - UIni, - UMenuEqualizer; + USongs, + UTexture, + UThemes, + UTime; type TScreenSong = class(TMenu) private Equalizer: Tms_Equalizer; + PreviewOpened: Integer; // interaction of the Song that is loaded for preview music + // -1 if nothing is opened + procedure StartMusicPreview(); procedure StopMusicPreview(); public @@ -65,7 +68,7 @@ type TextNumber: integer; //Video Icon Mod - VideoIcon: Cardinal; + VideoIcon: cardinal; TextCat: integer; StaticCat: integer; @@ -88,29 +91,28 @@ type Mode: TSingMode; //party Statics (Joker) - StaticTeam1Joker1: Cardinal; - StaticTeam1Joker2: Cardinal; - StaticTeam1Joker3: Cardinal; - StaticTeam1Joker4: Cardinal; - StaticTeam1Joker5: Cardinal; - - StaticTeam2Joker1: Cardinal; - StaticTeam2Joker2: Cardinal; - StaticTeam2Joker3: Cardinal; - StaticTeam2Joker4: Cardinal; - StaticTeam2Joker5: Cardinal; - - StaticTeam3Joker1: Cardinal; - StaticTeam3Joker2: Cardinal; - StaticTeam3Joker3: Cardinal; - StaticTeam3Joker4: Cardinal; - StaticTeam3Joker5: Cardinal; - - StaticParty: array of Cardinal; - TextParty: array of Cardinal; - StaticNonParty: array of Cardinal; - TextNonParty: array of Cardinal; - + StaticTeam1Joker1: cardinal; + StaticTeam1Joker2: cardinal; + StaticTeam1Joker3: cardinal; + StaticTeam1Joker4: cardinal; + StaticTeam1Joker5: cardinal; + + StaticTeam2Joker1: cardinal; + StaticTeam2Joker2: cardinal; + StaticTeam2Joker3: cardinal; + StaticTeam2Joker4: cardinal; + StaticTeam2Joker5: cardinal; + + StaticTeam3Joker1: cardinal; + StaticTeam3Joker2: cardinal; + StaticTeam3Joker3: cardinal; + StaticTeam3Joker4: cardinal; + StaticTeam3Joker5: cardinal; + + StaticParty: array of cardinal; + TextParty: array of cardinal; + StaticNonParty: array of cardinal; + TextNonParty: array of cardinal; constructor Create; override; procedure SetScroll; @@ -120,18 +122,19 @@ type procedure SetScroll4; procedure SetScroll5; procedure SetScroll6; - function ParseInput(PressedKey: Cardinal; CharCode: WideChar; PressedDown: Boolean): Boolean; override; + function ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; override; + function ParseMouse(MouseButton: integer; BtnDown: boolean; X, Y: integer): boolean; override; function Draw: boolean; override; procedure GenerateThumbnails(); - procedure onShow; override; - procedure onHide; override; - procedure SelectNext; - procedure SelectPrev; - procedure SkipTo(Target: Cardinal); + procedure OnShow; override; + procedure OnHide; override; + procedure SelectNext(UnloadCover: boolean); + procedure SelectPrev(UnloadCover: boolean); + procedure SkipTo(Target: cardinal); procedure FixSelected; //Show Wrong Song when Tabs on Fix procedure FixSelected2; //Show Wrong Song when Tabs on Fix - procedure ShowCatTL(Cat: Integer);// Show Cat in Top left - procedure ShowCatTLCustom(Caption: String);// Show Custom Text in Top left + procedure ShowCatTL(Cat: integer);// Show Cat in Top left + procedure ShowCatTLCustom(Caption: UTF8String);// Show Custom Text in Top left procedure HideCatTL;// Show Cat in Tob left procedure Refresh; //Refresh Song Sorting procedure ChangeMusic; @@ -142,7 +145,7 @@ type //procedures for Menu procedure StartSong; procedure OpenEditor; - procedure DoJoker(Team: Integer); + procedure DoJoker(Team: integer); procedure SelectPlayers; procedure UnloadDetailedCover; @@ -154,28 +157,31 @@ type implementation uses - UGraphic, - UMain, - UCovers, - math, + Math, gl, - USkins, + UCovers, UDLLManager, + UGraphic, + UMain, + UMenuButton, + UNote, UParty, UPlaylist, - UMenuButton, - UScreenSongMenu; + UScreenSongMenu, + USkins, + UUnicodeUtils; // ***** Public methods ****** // //Show Wrong Song when Tabs on Fix procedure TScreenSong.FixSelected; -var I, I2: Integer; +var + I, I2: integer; begin if CatSongs.VisibleSongs > 0 then begin I2:= 0; - for I := low(CatSongs.Song) to High(Catsongs.Song) do + for I := Low(CatSongs.Song) to High(Catsongs.Song) do begin if CatSongs.Song[I].Visible then inc(I2); @@ -190,12 +196,13 @@ begin end; procedure TScreenSong.FixSelected2; -var I, I2: Integer; +var + I, I2: integer; begin if CatSongs.VisibleSongs > 0 then begin I2:= 0; - for I := low(CatSongs.Song) to High(Catsongs.Song) do + for I := Low(CatSongs.Song) to High(Catsongs.Song) do begin if CatSongs.Song[I].Visible then inc(I2); @@ -209,15 +216,15 @@ begin end; //Show Wrong Song when Tabs on Fix End -procedure TScreenSong.ShowCatTLCustom(Caption: String);// Show Custom Text in Top left +procedure TScreenSong.ShowCatTLCustom(Caption: UTF8String);// Show Custom Text in Top left begin Text[TextCat].Text := Caption; Text[TextCat].Visible := true; - Static[StaticCat].Visible := False; + Static[StaticCat].Visible := false; end; //Show Cat in Top Left Mod -procedure TScreenSong.ShowCatTL(Cat: Integer); +procedure TScreenSong.ShowCatTL(Cat: integer); begin //Change Text[TextCat].Text := CatSongs.Song[Cat].Artist; @@ -225,7 +232,7 @@ begin //Show Text[TextCat].Visible := true; - Static[StaticCat].Visible := True; + Static[StaticCat].Visible := true; end; procedure TScreenSong.HideCatTL; @@ -234,20 +241,20 @@ begin //Text[TextCat].Visible := false; Static[StaticCat].Visible := false; //New -> Show Text specified in Theme - Text[TextCat].Visible := True; + Text[TextCat].Visible := true; Text[TextCat].Text := Theme.Song.TextCat.Text; end; //Show Cat in Top Left Mod End - -// Method for input parsing. If False is returned, GetNextWindow +// Method for input parsing. If false is returned, GetNextWindow // should be checked to know the next window to load; -function TScreenSong.ParseInput(PressedKey: Cardinal; CharCode: WideChar; PressedDown: Boolean): Boolean; +function TScreenSong.ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; var I: integer; I2: integer; - SDL_ModState: Word; - Letter: WideChar; + SDL_ModState: word; + UpperLetter: UCS4Char; + TempStr: UTF8String; begin Result := true; @@ -272,49 +279,56 @@ begin //Jump to Artist/Titel if ((SDL_ModState and KMOD_LALT <> 0) and (Mode = smNormal)) then begin - if (WideCharUpperCase(CharCode)[1] in ([WideChar('A')..WideChar('Z'), WideChar('0') .. WideChar('9')]) ) then + UpperLetter := UCS4UpperCase(CharCode); + + if (UpperLetter in ([Ord('A')..Ord('Z'), Ord('0') .. Ord('9')]) ) then begin - Letter := WideCharUpperCase(CharCode)[1]; I2 := Length(CatSongs.Song); //Jump To Titel if (SDL_ModState = (KMOD_LALT or KMOD_LSHIFT)) then begin - for I := 1 to high(CatSongs.Song) do + for I := 1 to High(CatSongs.Song) do begin - if (CatSongs.Song[(I + Interaction) mod I2].Visible) and - (Length(CatSongs.Song[(I + Interaction) mod I2].Title)>0) and - (WideStringUpperCase(CatSongs.Song[(I + Interaction) mod I2].Title)[1] = Letter) then + if (CatSongs.Song[(I + Interaction) mod I2].Visible) then begin - SkipTo(CatSongs.VisibleIndex((I + Interaction) mod I2)); + TempStr := CatSongs.Song[(I + Interaction) mod I2].Title; + if (Length(TempStr) > 0) and + (UCS4UpperCase(UTF8ToUCS4String(TempStr)[0]) = UpperLetter) then + begin + SkipTo(CatSongs.VisibleIndex((I + Interaction) mod I2)); - AudioPlayback.PlaySound(SoundLib.Change); + AudioPlayback.PlaySound(SoundLib.Change); - ChangeMusic; - SetScroll4; - //Break and Exit - Exit; + ChangeMusic; + SetScroll4; + //Break and Exit + Exit; + end; end; end; end //Jump to Artist else if (SDL_ModState = KMOD_LALT) then begin - for I := 1 to high(CatSongs.Song) do + for I := 1 to High(CatSongs.Song) do begin - if (CatSongs.Song[(I + Interaction) mod I2].Visible) and - (Length(CatSongs.Song[(I + Interaction) mod I2].Artist)>0) and - (WideStringUpperCase(CatSongs.Song[(I + Interaction) mod I2].Artist)[1] = Letter) then + if (CatSongs.Song[(I + Interaction) mod I2].Visible) then begin - SkipTo(CatSongs.VisibleIndex((I + Interaction) mod I2)); + TempStr := CatSongs.Song[(I + Interaction) mod I2].Artist; + if (Length(TempStr) > 0) and + (UCS4UpperCase(UTF8ToUCS4String(TempStr)[0]) = UpperLetter) then + begin + SkipTo(CatSongs.VisibleIndex((I + Interaction) mod I2)); - AudioPlayback.PlaySound(SoundLib.Change); + AudioPlayback.PlaySound(SoundLib.Change); - ChangeMusic; - SetScroll4; + ChangeMusic; + SetScroll4; - //Break and Exit - Exit; + //Break and Exit + Exit; + end; end; end; end; @@ -324,14 +338,14 @@ begin end; // check normal keys - case WideCharUpperCase(CharCode)[1] of - 'Q': + case UCS4UpperCase(CharCode) of + Ord('Q'): begin Result := false; Exit; end; - 'M': //Show SongMenu + Ord('M'): //Show SongMenu begin if (Songs.SongList.Count > 0) then begin @@ -341,121 +355,123 @@ begin begin if CatSongs.CatNumShow = -3 then begin - ScreenSongMenu.onShow; + ScreenSongMenu.OnShow; ScreenSongMenu.MenuShow(SM_Playlist); end else begin - ScreenSongMenu.onShow; + ScreenSongMenu.OnShow; ScreenSongMenu.MenuShow(SM_Main); end; end else begin - ScreenSongMenu.onShow; + ScreenSongMenu.OnShow; ScreenSongMenu.MenuShow(SM_Playlist_Load); end; end //Party Mode -> Show Party Menu else begin - ScreenSongMenu.onShow; + ScreenSongMenu.OnShow; ScreenSongMenu.MenuShow(SM_Party_Main); end; end; Exit; end; - 'P': //Show Playlist Menu + Ord('P'): //Show Playlist Menu begin if (Songs.SongList.Count > 0) and (Mode = smNormal) then begin - ScreenSongMenu.onShow; + ScreenSongMenu.OnShow; ScreenSongMenu.MenuShow(SM_Playlist_Load); end; Exit; end; - 'J': //Show Jumpto Menu + Ord('J'): //Show Jumpto Menu begin if (Songs.SongList.Count > 0) and (Mode = smNormal) then begin - ScreenSongJumpto.Visible := True; + ScreenSongJumpto.Visible := true; end; Exit; end; - 'E': + Ord('E'): begin OpenEditor; Exit; end; - 'R': + Ord('R'): begin - if (Songs.SongList.Count > 0) and (Mode = smNormal) then + if (Songs.SongList.Count > 0) and + (Mode = smNormal) then begin - if (SDL_ModState = KMOD_LSHIFT) and (Ini.Tabs_at_startup = 1) then //Random Category + if (SDL_ModState = KMOD_LSHIFT) and (Ini.TabsAtStartup = 1) then // random category begin - I2 := 0; //Count Cats - for I:= low(CatSongs.Song) to high (CatSongs.Song) do + I2 := 0; // count cats + for I := 0 to High(CatSongs.Song) do begin if CatSongs.Song[I].Main then Inc(I2); end; - I2 := Random (I2)+1; //Zufall + I2 := Random(I2 + 1); // random and include I2 - //Find Cat: - for I:= low(CatSongs.Song) to high (CatSongs.Song) do + // find cat: + for I := 0 to High(CatSongs.Song) do begin if CatSongs.Song[I].Main then Dec(I2); - if (I2<=0) then + if (I2 <= 0) then begin - //Show Cat in Top Left Mod + // show cat in top left mod ShowCatTL (I); Interaction := I; CatSongs.ShowCategoryList; CatSongs.ClickCategoryButton(I); - SelectNext; + SelectNext(true); FixSelected; break; end; end; end - else if (SDL_ModState = KMOD_LCTRL) and (Ini.Tabs_at_startup = 1) then //random in All Categorys + else if (SDL_ModState = KMOD_LCTRL) and (Ini.TabsAtStartup = 1) then // random in all categories begin repeat - I2 := Random(high(CatSongs.Song)+1) - low(CatSongs.Song)+1; - until CatSongs.Song[I2].Main = false; + I2 := Random(High(CatSongs.Song) + 1); + until (not CatSongs.Song[I2].Main); - //Search Cat - for I := I2 downto low(CatSongs.Song) do + // search cat + for I := I2 downto 0 do begin if CatSongs.Song[I].Main then break; end; - //In I is now the categorie in I2 the song + // in I is now the categorie in I2 the song - //Choose Cat + // choose cat CatSongs.ShowCategoryList; - //Show Cat in Top Left Mod + // show cat in top left mod ShowCatTL (I); CatSongs.ClickCategoryButton(I); - SelectNext; + SelectNext(true); - //Fix: Not Existing Song selected: - //if (I+1=I2) then Inc(I2); + // Fix: not existing song selected: + //if (I + 1 = I2) then + Inc(I2); - //Choose Song - SkipTo(I2-I); + // choose song + SkipTo(I2 - I); end - else //Random in one Category + else // random in one category begin SkipTo(Random(CatSongs.VisibleSongs)); end; @@ -476,20 +492,20 @@ begin if (Mode = smNormal) then begin //On Escape goto Cat-List Hack - if (Ini.Tabs_at_startup = 1) and (CatSongs.CatNumShow <> -1) then + if (Ini.TabsAtStartup = 1) and (CatSongs.CatNumShow <> -1) then begin //Find Category I := Interaction; - while not catsongs.Song[I].Main do - begin - Dec (I); - if (I < low(catsongs.Song)) then + while (not CatSongs.Song[I].Main) do + begin + Dec(I); + if (I < 0) then break; - end; - if (I<= 1) then - Interaction := high(catsongs.Song) + end; + if (I <= 1) then + Interaction := High(CatSongs.Song) else - Interaction := I - 1; + Interaction := I - 1; //Stop Music StopMusicPreview(); @@ -499,12 +515,11 @@ begin //Show Cat in Top Left Mod HideCatTL; - //Show Wrong Song when Tabs on Fix - SelectNext; + SelectNext(true); FixSelected; - //SelectPrev; - //CatSongs.Song[0].Visible := False; + //SelectPrev(true); + //CatSongs.Song[0].Visible := false; end else begin @@ -513,14 +528,14 @@ begin if (CatSongs.CatNumShow < -1) then begin //Atm: Set Empty Filter - CatSongs.SetFilter('', 0); + CatSongs.SetFilter('', fltAll); //Show Cat in Top Left Mod HideCatTL; Interaction := 0; //Show Wrong Song when Tabs on Fix - SelectNext; + SelectNext(true); FixSelected; ChangeMusic; @@ -544,7 +559,7 @@ begin end; SDLK_RETURN: begin - if Songs.SongList.Count > 0 then + if (Songs.SongList.Count > 0) then begin if CatSongs.Song[Interaction].Main then begin // clicked on Category Button @@ -560,7 +575,7 @@ begin // SetScroll4; //Show Wrong Song when Tabs on Fix - SelectNext; + SelectNext(true); FixSelected; //Play Music: @@ -601,16 +616,17 @@ begin if (CatSongs.CatNumShow > -2) then begin //Cat Change Hack - if Ini.Tabs_at_startup = 1 then + if Ini.TabsAtStartup = 1 then begin I := Interaction; - if I <= 0 then I := 1; + if I <= 0 then + I := 1; while not catsongs.Song[I].Main do begin Inc (I); - if (I > high(catsongs.Song)) then - I := low(catsongs.Song); + if (I > High(catsongs.Song)) then + I := Low(catsongs.Song); end; Interaction := I; @@ -619,7 +635,7 @@ begin ShowCatTL (Interaction); CatSongs.ClickCategoryButton(Interaction); - SelectNext; + SelectNext(true); FixSelected; //Play Music: @@ -641,19 +657,20 @@ begin if (CatSongs.CatNumShow > -2) then begin //Cat Change Hack - if Ini.Tabs_at_startup = 1 then + if Ini.TabsAtStartup = 1 then begin I := Interaction; I2 := 0; - if I <= 0 then I := 1; + if I <= 0 then + I := 1; while not catsongs.Song[I].Main or (I2 = 0) do begin if catsongs.Song[I].Main then Inc(I2); Dec (I); - if (I < low(catsongs.Song)) then - I := high(catsongs.Song); + if (I < Low(catsongs.Song)) then + I := High(catsongs.Song); end; Interaction := I; @@ -662,7 +679,7 @@ begin ShowCatTL (I); CatSongs.ClickCategoryButton(I); - SelectNext; + SelectNext(true); FixSelected; //Play Music: @@ -679,7 +696,7 @@ begin if (Songs.SongList.Count > 0) and (Mode = smNormal) then begin AudioPlayback.PlaySound(SoundLib.Change); - SelectNext; + SelectNext(true); //InteractNext; //SongTarget := Interaction; ChangeMusic; @@ -692,45 +709,117 @@ begin if (Songs.SongList.Count > 0)and (Mode = smNormal) then begin AudioPlayback.PlaySound(SoundLib.Change); - SelectPrev; + SelectPrev(true); ChangeMusic; SetScroll4; end; end; SDLK_1: - begin //Joker // to-do : Party - {if (Mode = smPartyMode) and (PartySession.Teams.NumTeams >= 1) and (PartySession.Teams.Teaminfo[0].Joker > 0) then - begin - //Use Joker - Dec(PartySession.Teams.Teaminfo[0].Joker); - SelectRandomSong; - SetJoker; - end; } + begin //Joker + DoJoker(0); end; SDLK_2: begin //Joker - {if (Mode = smPartyMode) and (PartySession.Teams.NumTeams >= 2) and (PartySession.Teams.Teaminfo[1].Joker > 0) then - begin - //Use Joker - Dec(PartySession.Teams.Teaminfo[1].Joker); - SelectRandomSong; - SetJoker; - end; } + DoJoker(1); end; SDLK_3: begin //Joker - {if (Mode = smPartyMode) and (PartySession.Teams.NumTeams >= 3) and (PartySession.Teams.Teaminfo[2].Joker > 0) then - begin - //Use Joker - Dec(PartySession.Teams.Teaminfo[2].Joker); - SelectRandomSong; - SetJoker; - end; } + DoJoker(2); end; end; + end; // if (PressedDown) +end; + +function TScreenSong.ParseMouse(MouseButton: integer; BtnDown: boolean; X, Y: integer): boolean; + var + I, J: Integer; + Btn: Integer; +begin + Result := true; + + if (ScreenSongMenu.Visible) then + begin + Result := ScreenSongMenu.ParseMouse(MouseButton, BtnDown, X, Y); + exit; + end + else if (ScreenSongJumpTo.Visible) then + begin + Result := ScreenSongJumpTo.ParseMouse(MouseButton, BtnDown, X, Y); + exit; + end + else // no extension visible + begin + if (BtnDown) then + begin + //if RightMbESC is set, send ESC keypress + if RightMbESC and (MouseButton = SDL_BUTTON_RIGHT) then + Result:=ParseInput(SDLK_ESCAPE, 0, true) + + //song scrolling with mousewheel + else if (MouseButton = SDL_BUTTON_WHEELDOWN) then + ParseInput(SDLK_RIGHT, 0, true) + + else if (MouseButton = SDL_BUTTON_WHEELUP) then + ParseInput(SDLK_LEFT, 0, true) + + //LMB anywhere starts + else if (MouseButton = SDL_BUTTON_LEFT) then + begin + if (CatSongs.VisibleSongs > 4) then + begin + // select the second visible button left from selected + I := 0; + Btn := Interaction; + while (I < 2) do + begin + Dec(Btn); + if (Btn < 0) then + Btn := High(CatSongs.Song); + + if (CatSongs.Song[Btn].Visible) then + Inc(I); + end; + + // test the 5 front buttons for click + for I := 0 to 4 do + begin + if InRegion(X, Y, Button[Btn].GetMouseOverArea) then + begin + // song cover clicked + if (I = 2) then + begin // Selected Song clicked -> start singing + ParseInput(SDLK_RETURN, 0, true); + end + else + begin // one of the other 4 covers in the front clicked -> select it + J := I - 2; + while (J < 0) do + begin + ParseInput(SDLK_LEFT, 0, true); + Inc(J); + end; + + while (J > 0) do + begin + ParseInput(SDLK_RIGHT, 0, true); + Dec(J); + end; + end; + Break; + end; + + Btn := CatSongs.FindNextVisible(Btn); + if (Btn = -1) then + Break; + end; + end + else + ParseInput(SDLK_RETURN, 0, true); + end; + end; end; end; @@ -795,24 +884,22 @@ begin GenerateThumbnails(); - // Randomize Patch Randomize; Equalizer := Tms_Equalizer.Create(AudioPlayback, Theme.Song.Equalizer); - if (Length(CatSongs.Song) > 0) then - Interaction := 0; + PreviewOpened := -1; end; procedure TScreenSong.GenerateThumbnails(); var - I: Integer; + I: integer; CoverButtonIndex: integer; CoverButton: TButton; - CoverName: string; CoverTexture: TTexture; Cover: TCover; + CoverFile: IPath; Song: TSong; begin if (Length(CatSongs.Song) <= 0) then @@ -827,7 +914,7 @@ begin CoverButton := nil; // create a clickable cover - CoverButtonIndex := AddButton(300 + I*250, 140, 200, 200, '', TEXTURE_TYPE_PLAIN, Theme.Song.Cover.Reflections); + CoverButtonIndex := AddButton(300 + I*250, 140, 200, 200, PATH_NONE, TEXTURE_TYPE_PLAIN, Theme.Song.Cover.Reflections); if (CoverButtonIndex > -1) then CoverButton := Button[CoverButtonIndex]; if (CoverButton = nil) then @@ -835,19 +922,17 @@ begin Song := CatSongs.Song[I]; - // if cover-image is not found then show 'no cover' - if (not FileExists(Song.Path + Song.Cover)) then - Song.Cover := ''; + CoverFile := Song.Path.Append(Song.Cover); + if (not CoverFile.IsFile()) then + Song.Cover := PATH_NONE; - if (Song.Cover = '') then - CoverName := Skin.GetTextureFileName('SongCover') - else - CoverName := Song.Path + Song.Cover; + if (Song.Cover.IsUnset) then + CoverFile := Skin.GetTextureFileName('SongCover'); // load cover and cache its texture - Cover := Covers.FindCover(CoverName); + Cover := Covers.FindCover(CoverFile); if (Cover = nil) then - Cover := Covers.AddCover(CoverName); + Cover := Covers.AddCover(CoverFile); // use the cached texture // TODO: this is a workaround until the new song-loading works. @@ -860,15 +945,22 @@ begin CoverTexture := Cover.GetPreviewTexture(); Texture.AddTexture(CoverTexture, TEXTURE_TYPE_PLAIN, true); CoverButton.Texture := CoverTexture; + + // set selected to false -> the right texture will be displayed + CoverButton.Selected := False; end; Cover.Free; end; + + // reset selection + if (Length(CatSongs.Song) > 0) then + Interaction := 0; end; procedure TScreenSong.SetScroll; var - VS, B: Integer; + VS, B: integer; begin VS := CatSongs.VisibleSongs; if VS > 0 then @@ -887,12 +979,12 @@ begin end; // Set visibility of video icon - Static[VideoIcon].Visible := (CatSongs.Song[Interaction].Video <> ''); + Static[VideoIcon].Visible := CatSongs.Song[Interaction].Video.IsSet; // Set texts Text[TextArtist].Text := CatSongs.Song[Interaction].Artist; Text[TextTitle].Text := CatSongs.Song[Interaction].Title; - if (Ini.Tabs_at_startup = 1) and (CatSongs.CatNumShow = -1) then + if (Ini.TabsAtStartup = 1) and (CatSongs.CatNumShow = -1) then begin Text[TextNumber].Text := IntToStr(CatSongs.Song[Interaction].OrderNum) + '/' + IntToStr(CatSongs.CatCount); Text[TextTitle].Text := '(' + IntToStr(CatSongs.Song[Interaction].CatNumber) + ' ' + Language.Translate('SING_SONGS_IN_CAT') + ')'; @@ -901,7 +993,7 @@ begin Text[TextNumber].Text := IntToStr(CatSongs.VisibleIndex(Interaction)+1) + '/' + IntToStr(VS) else if (CatSongs.CatNumShow = -3) then Text[TextNumber].Text := IntToStr(CatSongs.VisibleIndex(Interaction)+1) + '/' + IntToStr(VS) - else if (Ini.Tabs_at_startup = 1) then + else if (Ini.TabsAtStartup = 1) then Text[TextNumber].Text := IntToStr(CatSongs.Song[Interaction].CatNumber) + '/' + IntToStr(CatSongs.Song[Interaction - CatSongs.Song[Interaction].CatNumber].CatNumber) else Text[TextNumber].Text := IntToStr(Interaction+1) + '/' + IntToStr(Length(CatSongs.Song)); @@ -912,7 +1004,7 @@ begin Text[TextArtist].Text := ''; Text[TextTitle].Text := ''; for B := 0 to High(Button) do - Button[B].Visible := False; + Button[B].Visible := false; end; end; @@ -942,44 +1034,52 @@ begin VisCount := 0; for B := 0 to High(Button) do - if CatSongs.Song[B].Visible then Inc(VisCount); + if CatSongs.Song[B].Visible then + Inc(VisCount); VisInt := 0; for B := 0 to Interaction-1 do - if CatSongs.Song[B].Visible then Inc(VisInt); - + if CatSongs.Song[B].Visible then + Inc(VisInt); - if VisCount <= 6 then begin + if VisCount <= 6 then + begin Typ := 0; - end else begin - if VisInt <= 3 then begin + end + else + begin + if VisInt <= 3 then + begin Typ := 1; Count := 7; Ready := true; end; - if (VisCount - VisInt) <= 3 then begin + if (VisCount - VisInt) <= 3 then + begin Typ := 2; Count := 7; Ready := true; end; - if not Ready then begin + if not Ready then + begin Typ := 3; Src := Interaction; end; end; - // hide all buttons - for B := 0 to High(Button) do begin + for B := 0 to High(Button) do + begin Button[B].Visible := false; Button[B].Selectable := CatSongs.Song[B].Visible; end; { - for B := Src to Dst do begin + for B := Src to Dst do + begin //Button[B].Visible := true; Button[B].Visible := CatSongs.Song[B].Visible; Button[B].Selectable := Button[B].Visible; @@ -987,10 +1087,12 @@ begin end; } - - if Typ = 0 then begin - for B := 0 to High(Button) do begin - if CatSongs.Song[B].Visible then begin + if Typ = 0 then + begin + for B := 0 to High(Button) do + begin + if CatSongs.Song[B].Visible then + begin Button[B].Visible := true; Button[B].Y := 140 + (Placed) * 60; Inc(Placed); @@ -998,10 +1100,13 @@ begin end; end; - if Typ = 1 then begin + if Typ = 1 then + begin B := 0; - while (Count > 0) do begin - if CatSongs.Song[B].Visible then begin + while (Count > 0) do + begin + if CatSongs.Song[B].Visible then + begin Button[B].Visible := true; Button[B].Y := 140 + (Placed) * 60; Inc(Placed); @@ -1011,10 +1116,13 @@ begin end; end; - if Typ = 2 then begin + if Typ = 2 then + begin B := High(Button); - while (Count > 0) do begin - if CatSongs.Song[B].Visible then begin + while (Count > 0) do + begin + if CatSongs.Song[B].Visible then + begin Button[B].Visible := true; Button[B].Y := 140 + (6-Placed) * 60; Inc(Placed); @@ -1024,11 +1132,14 @@ begin end; end; - if Typ = 3 then begin + if Typ = 3 then + begin B := Src; Count := 4; - while (Count > 0) do begin - if CatSongs.Song[B].Visible then begin + while (Count > 0) do + begin + if CatSongs.Song[B].Visible then + begin Button[B].Visible := true; Button[B].Y := 140 + (3+Placed) * 60; Inc(Placed); @@ -1040,8 +1151,10 @@ begin B := Src-1; Placed := 0; Count := 3; - while (Count > 0) do begin - if CatSongs.Song[B].Visible then begin + while (Count > 0) do + begin + if CatSongs.Song[B].Visible then + begin Button[B].Visible := true; Button[B].Y := 140 + (2-Placed) * 60; Inc(Placed); @@ -1066,7 +1179,8 @@ begin for B := 0 to High(Button) do Button[B].X := 300 + (B - Interaction) * 260; - if Length(Button) >= 3 then begin + if Length(Button) >= 3 then + begin if Interaction = 0 then Button[High(Button)].X := 300 - 260; @@ -1076,7 +1190,8 @@ begin // circle { - for B := 0 to High(Button) do begin + for B := 0 to High(Button) do + begin Factor := (B - Interaction); // 0 to center, -1: to left, +1 to right Factor2 := Factor / Length(Button); Button[B].X := 300 + 10000 * sin(2*pi*Factor2); @@ -1099,13 +1214,14 @@ begin begin Button[B].X := 300 + (B - SongCurrent) * 260; if (Button[B].X < -Button[B].W) or (Button[B].X > 800) then - Button[B].Visible := False + Button[B].Visible := false else - Button[B].Visible := True; + Button[B].Visible := true; end; { - if Length(Button) >= 3 then begin + if Length(Button) >= 3 then + begin if Interaction = 0 then Button[High(Button)].X := 300 - 260; @@ -1116,7 +1232,8 @@ begin // circle { - for B := 0 to High(Button) do begin + for B := 0 to High(Button) do + begin Factor := (B - Interaction); // 0 to center, -1: to left, +1 to right Factor2 := Factor / Length(Button); Button[B].X := 300 + 10000 * sin(2*pi*Factor2); @@ -1173,10 +1290,10 @@ procedure TScreenSong.SetScroll5; var B: integer; Angle: real; - Pos: Real; + Pos: real; VS: integer; Padding: real; - X: Real; + X: real; { Theme.Song.CoverW: circle radius Theme.Song.CoverX: x-pos. of the left edge of the selected cover @@ -1248,13 +1365,13 @@ end; procedure TScreenSong.SetScroll6; // rotate (slotmachine style) var B: integer; - Angle: real; - Pos: Real; + Angle: real; + Pos: real; VS: integer; - diff: real; - X: Real; - Factor: real; - Z, Z2: real; + diff: real; + X: real; + Factor: real; + Z, Z2: real; begin VS := CatSongs.VisibleSongs; if VS <= 5 then @@ -1263,33 +1380,33 @@ begin for B := 0 to High(Button) do begin Button[B].Visible := CatSongs.Song[B].Visible; - if Button[B].Visible then begin // optimization for 1000 songs - updates only visible songs, hiding in tabs becomes useful for maintaing good speed - - Factor := 2 * pi * (CatSongs.VisibleIndex(B) - SongCurrent) / VS {CatSongs.VisibleSongs};// 0.5.0 (II): takes another 16ms - - Z := (1 + cos(Factor)) / 2; - Z2 := (1 + 2*Z) / 3; + if Button[B].Visible then // optimization for 1000 songs - updates only visible songs, hiding in tabs becomes useful for maintaing good speed + begin + + Factor := 2 * pi * (CatSongs.VisibleIndex(B) - SongCurrent) / VS {CatSongs.VisibleSongs};// 0.5.0 (II): takes another 16ms + Z := (1 + cos(Factor)) / 2; + Z2 := (1 + 2*Z) / 3; - Button[B].Y := Theme.Song.Cover.Y + (0.185 * Theme.Song.Cover.H * VS * sin(Factor)) * Z2 - ((Button[B].H - Theme.Song.Cover.H)/2); // 0.5.0 (I): 2 times faster by not calling CatSongs.VisibleSongs - Button[B].Z := Z / 2 + 0.3; + Button[B].Y := Theme.Song.Cover.Y + (0.185 * Theme.Song.Cover.H * VS * sin(Factor)) * Z2 - ((Button[B].H - Theme.Song.Cover.H)/2); // 0.5.0 (I): 2 times faster by not calling CatSongs.VisibleSongs + Button[B].Z := Z / 2 + 0.3; - Button[B].W := Theme.Song.Cover.H * Z2; + Button[B].W := Theme.Song.Cover.H * Z2; - //Button[B].Y := {50 +} 140 + 50 - 50 * Z2; - Button[B].X := Theme.Song.Cover.X + (Theme.Song.Cover.H - Abs(Button[B].H)) * 0.7 ; - Button[B].H := Button[B].W; + //Button[B].Y := {50 +} 140 + 50 - 50 * Z2; + Button[B].X := Theme.Song.Cover.X + (Theme.Song.Cover.H - Abs(Button[B].H)) * 0.7 ; + Button[B].H := Button[B].W; end; end; end else begin //Change Pos of all Buttons - for B := low(Button) to high(Button) do - begin - Button[B].Visible := CatSongs.Song[B].Visible; //Adjust Visibility + for B := Low(Button) to High(Button) do + begin + Button[B].Visible := CatSongs.Song[B].Visible; //Adjust Visibility if Button[B].Visible then //Only Change Pos for Visible Buttons - begin + begin Pos := (CatSongs.VisibleIndex(B) - SongCurrent); if (Pos < -VS/2) then Pos := Pos + VS @@ -1299,7 +1416,7 @@ begin if (Abs(Pos) < 2.5) then {fixed Positions} begin Angle := Pi * (Pos / 5); - //Button[B].Visible := False; + //Button[B].Visible := false; Button[B].H := Abs(Theme.Song.Cover.H * cos(Angle*0.8));//Power(Z2, 3); @@ -1313,7 +1430,6 @@ begin Diff := (Button[B].H - Theme.Song.Cover.H)/2; - X := Sin(Angle*1.3)*0.9; Button[B].Y := Theme.Song.Cover.Y + Theme.Song.Cover.W * X - Diff; @@ -1322,8 +1438,10 @@ begin begin {Behind the Front Covers} // limit-bg-covers hack - if (abs(VS/2-abs(Pos))>10) then Button[B].Visible:=False; - if VS > 25 then VS:=25; + if (abs(VS/2-abs(Pos))>10) then + Button[B].Visible := false; + if VS > 25 then + VS:=25; // end of limit-bg-covers hack if Pos < 0 then @@ -1341,7 +1459,6 @@ begin Button[B].X := Theme.Song.Cover.X - (Button[B].H - Theme.Song.Cover.H)*0.5; - Button[B].DeSelectReflectionspacing := 15 * Button[B].H/Theme.Song.Cover.H; Button[B].Y := Theme.Song.Cover.Y+Theme.Song.Cover.H/2-Button[b].H/2+Theme.Song.Cover.W/320*(Theme.Song.Cover.H*sin(Angle/2)*1.52); @@ -1351,8 +1468,7 @@ begin end; end; - -procedure TScreenSong.onShow; +procedure TScreenSong.OnShow; begin inherited; {** @@ -1366,7 +1482,7 @@ begin if Ini.Players = 4 then PlayersPlay := 6; //Cat Mod etc - if (Ini.Tabs_at_startup = 1) and (CatSongs.CatNumShow = -1) then + if (Ini.TabsAtStartup = 1) and (CatSongs.CatNumShow = -1) then begin CatSongs.ShowCategoryList; FixSelected; @@ -1378,7 +1494,9 @@ begin begin //Load Music only when Song Preview is activated if ( Ini.PreviewVolume <> 0 ) then - StartMusicPreview(); + StartMusicPreview() + else + PreviewOpened := -1; SetScroll; end; @@ -1389,7 +1507,7 @@ begin //If Playlist Shown -> Select Next automatically if (CatSongs.CatNumShow = -3) then begin - SelectNext; + SelectNext(true); ChangeMusic; end; end @@ -1409,15 +1527,15 @@ begin SetStatics; end; -procedure TScreenSong.onHide; +procedure TScreenSong.OnHide; begin + // if preview is not loaded: load musicfile now + if (PreviewOpened <> Interaction) then + AudioPlayback.Open(CatSongs.Song[Interaction].Path.Append(CatSongs.Song[Interaction].Mp3)); + // turn music volume to 100% AudioPlayback.SetVolume(1.0); - // if preview is deactivated: load musicfile now - If (IPreviewVolumeVals[Ini.PreviewVolume] = 0) then - AudioPlayback.Open(CatSongs.Song[Interaction].Path + CatSongs.Song[Interaction].Mp3); - // if hide then stop music (for party mode popup on exit) if (Display.NextScreen <> @ScreenSing) {and (Display.NextScreen <> @ScreenSingModi) }then @@ -1441,9 +1559,9 @@ end; function TScreenSong.Draw: boolean; var - dx: real; - dt: real; - I: Integer; + dx: real; + dt: real; + I: integer; begin dx := SongTarget-SongCurrent; dt := TimeSkip * 7; @@ -1454,7 +1572,8 @@ begin SongCurrent := SongCurrent + dx*dt; { - if SongCurrent > Catsongs.VisibleSongs then begin + if SongCurrent > Catsongs.VisibleSongs then + begin SongCurrent := SongCurrent - Catsongs.VisibleSongs; SongTarget := SongTarget - Catsongs.VisibleSongs; end; @@ -1518,16 +1637,17 @@ begin Result := true; end; -procedure TScreenSong.SelectNext; +procedure TScreenSong.SelectNext(UnloadCover: boolean); var - Skip: integer; - VS: Integer; + Skip: integer; + VS: integer; begin VS := CatSongs.VisibleSongs; if VS > 0 then begin - UnLoadDetailedCover; + if UnloadCover then //that should fix the performance problem on scrolling + UnLoadDetailedCover; Skip := 1; @@ -1540,7 +1660,8 @@ begin Interaction := (Interaction + Skip) mod Length(Interactions); // try to keep all at the beginning - if SongTarget > VS-1 then begin + if SongTarget > VS-1 then + begin SongTarget := SongTarget - VS; SongCurrent := SongCurrent - VS; end; @@ -1552,26 +1673,29 @@ begin //Button[Interaction].Texture := Texture.GetTexture(Button[Interaction].Texture.Name, TEXTURE_TYPE_PLAIN, false); end; -procedure TScreenSong.SelectPrev; +procedure TScreenSong.SelectPrev(UnloadCover: boolean); var - Skip: integer; - VS: Integer; + Skip: integer; + VS: integer; begin VS := CatSongs.VisibleSongs; if VS > 0 then begin - UnLoadDetailedCover; + if UnloadCover then + UnLoadDetailedCover; //that should fix the performance problem on scrolling Skip := 1; - while (not CatSongs.Song[(Interaction - Skip + Length(Interactions)) mod Length(Interactions)].Visible) do Inc(Skip); + while (not CatSongs.Song[(Interaction - Skip + Length(Interactions)) mod Length(Interactions)].Visible) do + Inc(Skip); SongTarget := SongTarget - 1;//Skip; Interaction := (Interaction - Skip + Length(Interactions)) mod Length(Interactions); // try to keep all at the beginning - if SongTarget < 0 then begin + if SongTarget < 0 then + begin SongTarget := SongTarget + CatSongs.VisibleSongs; SongCurrent := SongCurrent + CatSongs.VisibleSongs; end; @@ -1591,8 +1715,10 @@ begin if not assigned(Song) then Exit; - if AudioPlayback.Open(Song.Path + Song.Mp3) then + if AudioPlayback.Open(Song.Path.Append(Song.Mp3)) then begin + PreviewOpened := Interaction; + AudioPlayback.Position := AudioPlayback.Length / 4; // set preview volume if (Ini.PreviewFading = 0) then @@ -1640,6 +1766,7 @@ end; procedure TScreenSong.ChangeMusic; begin StopMusicPreview(); + PreviewOpened := -1; // Preview song if activated and current selection is not a category cover if (CatSongs.VisibleSongs > 0) and @@ -1651,9 +1778,9 @@ begin end; end; -procedure TScreenSong.SkipTo(Target: Cardinal); +procedure TScreenSong.SkipTo(Target: cardinal); var - i: integer; + i: integer; begin UnLoadDetailedCover; @@ -1661,62 +1788,61 @@ begin SongTarget := 0; for i := 1 to Target+1 do - SelectNext; + SelectNext(false); FixSelected2; end; procedure TScreenSong.SelectRandomSong; var - I, I2: Integer; + I, I2: integer; begin case PlaylistMan.Mode of - smNormal: //All Songs Just Select Random Song + smNormal: // all songs just select random song begin - //When Tabs are activated then use Tab Method - if (Ini.Tabs_at_startup = 1) then + // when tabs are activated then use tab method + if (Ini.TabsAtStartup = 1) then begin repeat - I2 := Random(high(CatSongs.Song)+1) - low(CatSongs.Song)+1; + I2 := Low(CatSongs.Song) + Random(High(CatSongs.Song) + 1 - Low(CatSongs.Song)); until CatSongs.Song[I2].Main = false; - //Search Cat - for I := I2 downto low(CatSongs.Song) do + // search cat + for I := I2 downto Low(CatSongs.Song) do begin if CatSongs.Song[I].Main then break; end; - //In I ist jetzt die Kategorie in I2 der Song - //I is the CatNum, I2 is the No of the Song within this Cat + // I is the cat number, I2 is the no of the song within this cat - //Choose Cat + // choose cat CatSongs.ShowCategoryList; - //Show Cat in Top Left Mod - ShowCatTL (I); + // show cat in top left mod + ShowCatTL(I); CatSongs.ClickCategoryButton(I); - SelectNext; + SelectNext(true); - //Choose Song - SkipTo(I2-I); + // choose song + SkipTo(I2 - I); end - //When Tabs are deactivated use easy Method + // when tabs are deactivated use easy method else SkipTo(Random(CatSongs.VisibleSongs)); end; - smPartyMode: //One Category Select Category and Select Random Song + smPartyMode: // one category select category and select random song begin CatSongs.ShowCategoryList; CatSongs.ClickCategoryButton(PlaylistMan.CurPlayList); ShowCatTL(PlaylistMan.CurPlayList); - SelectNext; + SelectNext(true); FixSelected2; SkipTo(Random(CatSongs.VisibleSongs)); end; - smPlaylistRandom: //Playlist: Select Playlist and Select Random Song + smPlaylistRandom: // playlist: select playlist and select random song begin PlaylistMan.SetPlayList(PlaylistMan.CurPlayList); @@ -1745,11 +1871,11 @@ begin end else begin - Static[StaticTeam1Joker1].Visible := False; - Static[StaticTeam1Joker2].Visible := False; - Static[StaticTeam1Joker3].Visible := False; - Static[StaticTeam1Joker4].Visible := False; - Static[StaticTeam1Joker5].Visible := False; + Static[StaticTeam1Joker1].Visible := false; + Static[StaticTeam1Joker2].Visible := false; + Static[StaticTeam1Joker3].Visible := false; + Static[StaticTeam1Joker4].Visible := false; + Static[StaticTeam1Joker5].Visible := false; end; if (Length(Party.Teams) >= 2) then @@ -1762,11 +1888,11 @@ begin end else begin - Static[StaticTeam2Joker1].Visible := False; - Static[StaticTeam2Joker2].Visible := False; - Static[StaticTeam2Joker3].Visible := False; - Static[StaticTeam2Joker4].Visible := False; - Static[StaticTeam2Joker5].Visible := False; + Static[StaticTeam2Joker1].Visible := false; + Static[StaticTeam2Joker2].Visible := false; + Static[StaticTeam2Joker3].Visible := false; + Static[StaticTeam2Joker4].Visible := false; + Static[StaticTeam2Joker5].Visible := false; end; if (Length(Party.Teams) >= 3) then @@ -1779,57 +1905,56 @@ begin end else begin - Static[StaticTeam3Joker1].Visible := False; - Static[StaticTeam3Joker2].Visible := False; - Static[StaticTeam3Joker3].Visible := False; - Static[StaticTeam3Joker4].Visible := False; - Static[StaticTeam3Joker5].Visible := False; + Static[StaticTeam3Joker1].Visible := false; + Static[StaticTeam3Joker2].Visible := false; + Static[StaticTeam3Joker3].Visible := false; + Static[StaticTeam3Joker4].Visible := false; + Static[StaticTeam3Joker5].Visible := false; end; - end else begin //Hide all - Static[StaticTeam1Joker1].Visible := False; - Static[StaticTeam1Joker2].Visible := False; - Static[StaticTeam1Joker3].Visible := False; - Static[StaticTeam1Joker4].Visible := False; - Static[StaticTeam1Joker5].Visible := False; - - Static[StaticTeam2Joker1].Visible := False; - Static[StaticTeam2Joker2].Visible := False; - Static[StaticTeam2Joker3].Visible := False; - Static[StaticTeam2Joker4].Visible := False; - Static[StaticTeam2Joker5].Visible := False; - - Static[StaticTeam3Joker1].Visible := False; - Static[StaticTeam3Joker2].Visible := False; - Static[StaticTeam3Joker3].Visible := False; - Static[StaticTeam3Joker4].Visible := False; - Static[StaticTeam3Joker5].Visible := False; + Static[StaticTeam1Joker1].Visible := false; + Static[StaticTeam1Joker2].Visible := false; + Static[StaticTeam1Joker3].Visible := false; + Static[StaticTeam1Joker4].Visible := false; + Static[StaticTeam1Joker5].Visible := false; + + Static[StaticTeam2Joker1].Visible := false; + Static[StaticTeam2Joker2].Visible := false; + Static[StaticTeam2Joker3].Visible := false; + Static[StaticTeam2Joker4].Visible := false; + Static[StaticTeam2Joker5].Visible := false; + + Static[StaticTeam3Joker1].Visible := false; + Static[StaticTeam3Joker2].Visible := false; + Static[StaticTeam3Joker3].Visible := false; + Static[StaticTeam3Joker4].Visible := false; + Static[StaticTeam3Joker5].Visible := false; end; end; procedure TScreenSong.SetStatics; var - I: Integer; - Visible: Boolean; + I: integer; + Visible: boolean; begin //Set Visibility of Party Statics and Text Visible := (Mode = smPartyMode); - for I := 0 to high(StaticParty) do + for I := 0 to High(StaticParty) do Static[StaticParty[I]].Visible := Visible; - for I := 0 to high(TextParty) do + for I := 0 to High(TextParty) do Text[TextParty[I]].Visible := Visible; //Set Visibility of Non Party Statics and Text Visible := not Visible; - for I := 0 to high(StaticNonParty) do + for I := 0 to High(StaticNonParty) do Static[StaticNonParty[I]].Visible := Visible; - for I := 0 to high(TextNonParty) do + for I := 0 to High(TextNonParty) do Text[TextNonParty[I]].Visible := Visible; end; @@ -1856,7 +1981,7 @@ begin CatSongs.Selected := Interaction; StopMusicPreview(); - ScreenName.Goto_SingScreen := True; + ScreenName.Goto_SingScreen := true; FadeTo(@ScreenName); end; @@ -1874,9 +1999,8 @@ begin end; //Team No of Team (0-5) -procedure TScreenSong.DoJoker (Team: Integer); +procedure TScreenSong.DoJoker (Team: integer); begin - if (Mode = smPartyMode) and (High(Party.Teams) >= Team) and (Party.Teams[Team].JokersLeft > 0) then @@ -1886,7 +2010,6 @@ begin SelectRandomSong; SetJoker; end; - end; //Detailed Cover Unloading. Unloads the Detailed, uncached Cover of the cur. Song @@ -1908,7 +2031,7 @@ begin CatSongs.Refresh; CatSongs.ShowCategoryList; Interaction := 0; - SelectNext; + SelectNext(true); FixSelected; } end; diff --git a/Lua/src/screens/UScreenSongJumpto.pas b/Lua/src/screens/UScreenSongJumpto.pas index a8679368..7f82bbec 100644 --- a/Lua/src/screens/UScreenSongJumpto.pas +++ b/Lua/src/screens/UScreenSongJumpto.pas @@ -34,86 +34,93 @@ interface {$I switches.inc} uses - UMenu, SDL, UDisplay, UMusic, UFiles, SysUtils, UThemes; + SDL, + SysUtils, + UMenu, + UDisplay, + UMusic, + UFiles, + USongs, + UThemes; type TScreenSongJumpto = class(TMenu) private //For ChangeMusic - LastPlayed: Integer; - VisibleBool: Boolean; - public - VisSongs: Integer; + fLastPlayed: integer; + fVisible: boolean; + fSelectType: TSongFilter; + fVisSongs: integer; - constructor Create; override; + procedure SetTextFound(Count: Cardinal); //Visible //Whether the Menu should be Drawn //Whether the Menu should be Drawn - procedure SetVisible(Value: Boolean); - property Visible: Boolean read VisibleBool write SetVisible; + procedure SetVisible(Value: boolean); + public + constructor Create; override; - function ParseInput(PressedKey: Cardinal; CharCode: WideChar; PressedDown: Boolean): Boolean; override; - procedure onShow; override; + function ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; override; + procedure OnShow; override; function Draw: boolean; override; - procedure SetTextFound(const Count: Cardinal); + property Visible: boolean read fVisible write SetVisible; end; -var - IType: Array [0..2] of String; - SelectType: Integer; - - implementation -uses UGraphic, UMain, UIni, UTexture, ULanguage, UParty, USongs, UScreenSong, ULog; - -function TScreenSongJumpto.ParseInput(PressedKey: Cardinal; CharCode: WideChar; PressedDown: Boolean): Boolean; +uses + UGraphic, + UMain, + UIni, + UTexture, + ULanguage, + UParty, + UScreenSong, + ULog, + UUnicodeUtils; + +function TScreenSongJumpto.ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; begin Result := true; - If (PressedDown) Then + if (PressedDown) then begin // Key Down // check normal keys - case CharCode of - '0'..'9', 'a'..'z', 'A'..'Z', ' ', '-', '_', '!', ',', '<', '/', '*', '?', '''', '"', - '[', '{', ';', ':': - begin - if Interaction = 0 then - begin - Button[0].Text[0].Text := Button[0].Text[0].Text + CharCode; - SetTextFound(CatSongs.SetFilter(Button[0].Text[0].Text, SelectType)); - end; - end; + if (IsAlphaNumericChar(CharCode) or + IsPunctuationChar(CharCode)) then + begin + if (Interaction = 0) then + begin + Button[0].Text[0].Text := Button[0].Text[0].Text + UCS4ToUTF8String(CharCode); + SetTextFound(CatSongs.SetFilter(Button[0].Text[0].Text, fSelectType)); + end; end; // check special keys case PressedKey of SDLK_BACKSPACE: begin - if (Interaction = 0) AND (Length(Button[0].Text[0].Text) > 0) then + if (Interaction = 0) and (Length(Button[0].Text[0].Text) > 0) then begin - Button[0].Text[0].DeleteLastL; - SetTextFound(CatSongs.SetFilter(Button[0].Text[0].Text, SelectType)); + Button[0].Text[0].DeleteLastLetter(); + SetTextFound(CatSongs.SetFilter(Button[0].Text[0].Text, fSelectType)); end; end; SDLK_RETURN, SDLK_ESCAPE: begin - Visible := False; + Visible := false; AudioPlayback.PlaySound(SoundLib.Back); - if (VisSongs = 0) AND (Length(Button[0].Text[0].Text) > 0) then + if (fVisSongs = 0) and (Length(Button[0].Text[0].Text) > 0) then begin ScreenSong.UnLoadDetailedCover; Button[0].Text[0].Text := ''; - CatSongs.SetFilter('', 0); + CatSongs.SetFilter('', fltAll); SetTextFound(0); end; end; - // Up and Down could be done at the same time, - // but I don't want to declare variables inside - // functions like this one, called so many times SDLK_DOWN: begin {SelectNext; @@ -131,7 +138,7 @@ begin Interaction := 1; InteractInc; if (Length(Button[0].Text[0].Text) > 0) then - SetTextFound(CatSongs.SetFilter(Button[0].Text[0].Text, SelectType)); + SetTextFound(CatSongs.SetFilter(Button[0].Text[0].Text, fSelectType)); Interaction := 0; end; SDLK_LEFT: @@ -139,7 +146,7 @@ begin Interaction := 1; InteractDec; if (Length(Button[0].Text[0].Text) > 0) then - SetTextFound(CatSongs.SetFilter(Button[0].Text[0].Text, SelectType)); + SetTextFound(CatSongs.SetFilter(Button[0].Text[0].Text, fSelectType)); Interaction := 0; end; end; @@ -147,8 +154,6 @@ begin end; constructor TScreenSongJumpto.Create; -//var -// I: integer; // Auto Removed, Unused Variable begin inherited Create; @@ -160,24 +165,23 @@ begin if (Length(Button[0].Text) = 0) then AddButtonText(14, 20, ''); - SelectType := 0; - AddSelectSlide(Theme.SongJumpto.SelectSlideType, SelectType, Theme.SongJumpto.IType); - + fSelectType := fltAll; + AddSelectSlide(Theme.SongJumpto.SelectSlideType, PInteger(@fSelectType)^, Theme.SongJumpto.IType); Interaction := 0; - LastPlayed := 0; + fLastPlayed := 0; end; -procedure TScreenSongJumpto.SetVisible(Value: Boolean); +procedure TScreenSongJumpto.SetVisible(Value: boolean); begin -//If change from unvisible to Visible then OnShow - if (VisibleBool = False) AND (Value = True) then +//If change from invisible to Visible then OnShow + if (fVisible = false) and (Value = true) then OnShow; - VisibleBool := Value; + fVisible := Value; end; -procedure TScreenSongJumpto.onShow; +procedure TScreenSongJumpto.OnShow; begin inherited; @@ -192,9 +196,9 @@ begin //Select Input Interaction := 0; - Button[0].Text[0].Selected := True; + Button[0].Text[0].Selected := true; - LastPlayed := ScreenSong.Interaction; + fLastPlayed := ScreenSong.Interaction; end; function TScreenSongJumpto.Draw: boolean; @@ -202,7 +206,7 @@ begin Result := inherited Draw; end; -procedure TScreenSongJumpto.SetTextFound(const Count: Cardinal); +procedure TScreenSongJumpto.SetTextFound(Count: cardinal); begin if (Count = 0) then begin @@ -220,19 +224,18 @@ begin ScreenSong.ShowCatTLCustom(Format(Theme.SongJumpto.CatText, [Button[0].Text[0].Text])); end; - //Set visSongs - VisSongs := Count; + fVisSongs := Count; //Fix SongSelection ScreenSong.Interaction := high(CatSongs.Song); - ScreenSong.SelectNext; + ScreenSong.SelectNext(true); ScreenSong.FixSelected; //Play Correct Music - if (ScreenSong.Interaction <> LastPlayed) then + if (ScreenSong.Interaction <> fLastPlayed) then begin - LastPlayed := ScreenSong.Interaction; + fLastPlayed := ScreenSong.Interaction; ScreenSong.ChangeMusic; end; diff --git a/Lua/src/screens/UScreenSongMenu.pas b/Lua/src/screens/UScreenSongMenu.pas index 72be93db..173ac2c8 100644 --- a/Lua/src/screens/UScreenSongMenu.pas +++ b/Lua/src/screens/UScreenSongMenu.pas @@ -45,79 +45,81 @@ uses type TScreenSongMenu = class(TMenu) private - CurMenu: Byte; //Num of the cur. Shown Menu + CurMenu: byte; // num of the cur. shown menu public - Visible: Boolean; //Whether the Menu should be Drawn + Visible: boolean; // whether the menu should be drawn constructor Create; override; - function ParseInput(PressedKey: Cardinal; CharCode: WideChar; PressedDown: Boolean): Boolean; override; - procedure onShow; override; + function ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; override; + procedure OnShow; override; function Draw: boolean; override; - procedure MenuShow(sMenu: Byte); + procedure MenuShow(sMenu: byte); procedure HandleReturn; end; const SM_Main = 1; - - SM_PlayList = 64 or 1; - SM_Playlist_Add = 64 or 2; - SM_Playlist_New = 64 or 3; - SM_Playlist_DelItem = 64 or 5; + SM_PlayList = 64 or 1; + SM_Playlist_Add = 64 or 2; + SM_Playlist_New = 64 or 3; - SM_Playlist_Load = 64 or 8 or 1; - SM_Playlist_Del = 64 or 8 or 5; + SM_Playlist_DelItem = 64 or 5; + SM_Playlist_Load = 64 or 8 or 1; + SM_Playlist_Del = 64 or 8 or 5; - SM_Party_Main = 128 or 1; - SM_Party_Joker = 128 or 2; + SM_Party_Main = 128 or 1; + SM_Party_Joker = 128 or 2; var - ISelections: Array of String; - SelectValue: Integer; - + ISelections: array of UTF8String; + SelectValue: integer; implementation -uses UGraphic, - UMain, - UIni, - UTexture, - ULanguage, - UParty, - UPlaylist, - USongs; - -function TScreenSongMenu.ParseInput(PressedKey: Cardinal; CharCode: WideChar; PressedDown: Boolean): Boolean; +uses + UGraphic, + UMain, + UIni, + UTexture, + ULanguage, + UParty, + UPlaylist, + USongs, + UUnicodeUtils; + +function TScreenSongMenu.ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; begin Result := true; if (PressedDown) then - begin // Key Down - if (CurMenu = SM_Playlist_New) AND (Interaction=0) then + begin // key down + if (CurMenu = SM_Playlist_New) and (Interaction=0) then begin // check normal keys - case WideCharUpperCase(CharCode)[1] of - '0'..'9', 'A'..'Z', ' ', '-', '_', '!', ',', '<', '/', '*', '?', '''', '"': - begin - Button[Interaction].Text[0].Text := Button[Interaction].Text[0].Text + CharCode; - exit; - end; + if IsAlphaNumericChar(CharCode) or + (CharCode in [Ord(' '), Ord('-'), Ord('_'), Ord('!'), + Ord(','), Ord('<'), Ord('/'), Ord('*'), + Ord('?'), Ord(''''), Ord('"')]) then + begin + Button[Interaction].Text[0].Text := Button[Interaction].Text[0].Text + + UCS4ToUTF8String(CharCode); + exit; end; // check special keys case PressedKey of SDLK_BACKSPACE: begin - Button[Interaction].Text[0].DeleteLastL; + Button[Interaction].Text[0].DeleteLastLetter; exit; end; end; end; // check normal keys - case WideCharUpperCase(CharCode)[1] of - 'Q': + case UCS4UpperCase(CharCode) of + Ord('Q'): begin Result := false; Exit; @@ -127,10 +129,10 @@ begin // check special keys case PressedKey of SDLK_ESCAPE, - SDLK_BACKSPACE : + SDLK_BACKSPACE: begin AudioPlayback.PlaySound(SoundLib.Back); - Visible := False; + Visible := false; end; SDLK_RETURN: @@ -138,8 +140,8 @@ begin HandleReturn; end; - SDLK_DOWN: InteractNext; - SDLK_UP: InteractPrev; + SDLK_DOWN: InteractNext; + SDLK_UP: InteractPrev; SDLK_RIGHT: begin @@ -153,8 +155,8 @@ begin end; SDLK_1: - begin //Jocker - //Use Joker + begin // jocker + // use joker case CurMenu of SM_Party_Main: begin @@ -163,8 +165,8 @@ begin end; end; SDLK_2: - begin //Jocker - //Use Joker + begin // jocker + // use joker case CurMenu of SM_Party_Main: begin @@ -173,8 +175,8 @@ begin end; end; SDLK_3: - begin //Jocker - //Use Joker + begin // jocker + // use joker case CurMenu of SM_Party_Main: begin @@ -189,12 +191,11 @@ end; constructor TScreenSongMenu.Create; begin inherited Create; - - //Create Dummy SelectSlide Entrys + + // create dummy selectslide entrys SetLength(ISelections, 1); ISelections[0] := 'Dummy'; - AddText(Theme.SongMenu.TextMenu); LoadFromTheme(Theme.SongMenu); @@ -217,7 +218,6 @@ begin if (Length(Button[3].Text) = 0) then AddButtonText(14, 20, 'Button 4'); - Interaction := 0; end; @@ -226,27 +226,26 @@ begin Result := inherited Draw; end; -procedure TScreenSongMenu.onShow; +procedure TScreenSongMenu.OnShow; begin inherited; - end; -procedure TScreenSongMenu.MenuShow(sMenu: Byte); +procedure TScreenSongMenu.MenuShow(sMenu: byte); begin - Interaction := 0; //Reset Interaction - Visible := True; //Set Visible - Case sMenu of + Interaction := 0; // reset interaction + Visible := true; // set visible + case sMenu of SM_Main: begin CurMenu := sMenu; Text[0].Text := Language.Translate('SONG_MENU_NAME_MAIN'); - Button[0].Visible := True; - Button[1].Visible := True; - Button[2].Visible := True; - Button[3].Visible := True; - SelectsS[0].Visible := False; + Button[0].Visible := true; + Button[1].Visible := true; + Button[2].Visible := true; + Button[3].Visible := true; + SelectsS[0].Visible := false; Button[0].Text[0].Text := Language.Translate('SONG_MENU_PLAY'); Button[1].Text[0].Text := Language.Translate('SONG_MENU_CHANGEPLAYERS'); @@ -259,11 +258,11 @@ begin CurMenu := sMenu; Text[0].Text := Language.Translate('SONG_MENU_NAME_PLAYLIST'); - Button[0].Visible := True; - Button[1].Visible := True; - Button[2].Visible := True; - Button[3].Visible := True; - SelectsS[0].Visible := False; + Button[0].Visible := true; + Button[1].Visible := true; + Button[2].Visible := true; + Button[3].Visible := true; + SelectsS[0].Visible := false; Button[0].Text[0].Text := Language.Translate('SONG_MENU_PLAY'); Button[1].Text[0].Text := Language.Translate('SONG_MENU_CHANGEPLAYERS'); @@ -276,11 +275,11 @@ begin CurMenu := sMenu; Text[0].Text := Language.Translate('SONG_MENU_NAME_PLAYLIST_ADD'); - Button[0].Visible := True; - Button[1].Visible := False; - Button[2].Visible := False; - Button[3].Visible := True; - SelectsS[0].Visible := True; + Button[0].Visible := true; + Button[1].Visible := false; + Button[2].Visible := false; + Button[3].Visible := true; + SelectsS[0].Visible := true; Button[0].Text[0].Text := Language.Translate('SONG_MENU_PLAYLIST_ADD_NEW'); Button[3].Text[0].Text := Language.Translate('SONG_MENU_PLAYLIST_ADD_EXISTING'); @@ -294,9 +293,9 @@ begin end else begin - Button[3].Visible := False; - SelectsS[0].Visible := False; - Button[2].Visible := True; + Button[3].Visible := false; + SelectsS[0].Visible := false; + Button[2].Visible := true; Button[2].Text[0].Text := Language.Translate('SONG_MENU_PLAYLIST_NOEXISTING'); end; end; @@ -306,11 +305,11 @@ begin CurMenu := sMenu; Text[0].Text := Language.Translate('SONG_MENU_NAME_PLAYLIST_NEW'); - Button[0].Visible := True; - Button[1].Visible := False; - Button[2].Visible := True; - Button[3].Visible := True; - SelectsS[0].Visible := False; + Button[0].Visible := true; + Button[1].Visible := false; + Button[2].Visible := true; + Button[3].Visible := true; + SelectsS[0].Visible := false; Button[0].Text[0].Text := Language.Translate('SONG_MENU_PLAYLIST_NEW_UNNAMED'); Button[2].Text[0].Text := Language.Translate('SONG_MENU_PLAYLIST_NEW_CREATE'); @@ -322,11 +321,11 @@ begin CurMenu := sMenu; Text[0].Text := Language.Translate('SONG_MENU_NAME_PLAYLIST_DELITEM'); - Button[0].Visible := True; - Button[1].Visible := False; - Button[2].Visible := False; - Button[3].Visible := True; - SelectsS[0].Visible := False; + Button[0].Visible := true; + Button[1].Visible := false; + Button[2].Visible := false; + Button[3].Visible := true; + SelectsS[0].Visible := false; Button[0].Text[0].Text := Language.Translate('SONG_MENU_YES'); Button[3].Text[0].Text := Language.Translate('SONG_MENU_CANCEL'); @@ -337,13 +336,13 @@ begin CurMenu := sMenu; Text[0].Text := Language.Translate('SONG_MENU_NAME_PLAYLIST_LOAD'); - //Show Delete Curent Playlist Button when Playlist is opened + // show delete curent playlist button when playlist is opened Button[0].Visible := (CatSongs.CatNumShow = -3); - Button[1].Visible := False; - Button[2].Visible := False; - Button[3].Visible := True; - SelectsS[0].Visible := True; + Button[1].Visible := false; + Button[2].Visible := false; + Button[3].Visible := true; + SelectsS[0].Visible := true; Button[0].Text[0].Text := Language.Translate('SONG_MENU_PLAYLIST_DELCURRENT'); Button[3].Text[0].Text := Language.Translate('SONG_MENU_PLAYLIST_LOAD'); @@ -358,9 +357,9 @@ begin end else begin - Button[3].Visible := False; - SelectsS[0].Visible := False; - Button[2].Visible := True; + Button[3].Visible := false; + SelectsS[0].Visible := false; + Button[2].Visible := true; Button[2].Text[0].Text := Language.Translate('SONG_MENU_PLAYLIST_NOEXISTING'); Interaction := 2; end; @@ -371,27 +370,26 @@ begin CurMenu := sMenu; Text[0].Text := Language.Translate('SONG_MENU_NAME_PLAYLIST_DEL'); - Button[0].Visible := True; - Button[1].Visible := False; - Button[2].Visible := False; - Button[3].Visible := True; - SelectsS[0].Visible := False; + Button[0].Visible := true; + Button[1].Visible := false; + Button[2].Visible := false; + Button[3].Visible := true; + SelectsS[0].Visible := false; Button[0].Text[0].Text := Language.Translate('SONG_MENU_YES'); Button[3].Text[0].Text := Language.Translate('SONG_MENU_CANCEL'); end; - SM_Party_Main: begin CurMenu := sMenu; Text[0].Text := Language.Translate('SONG_MENU_NAME_PARTY_MAIN'); - Button[0].Visible := True; - Button[1].Visible := False; - Button[2].Visible := False; - Button[3].Visible := True; - SelectsS[0].Visible := False; + Button[0].Visible := true; + Button[1].Visible := false; + Button[2].Visible := false; + Button[3].Visible := true; + SelectsS[0].Visible := false; Button[0].Text[0].Text := Language.Translate('SONG_MENU_PLAY'); //Button[1].Text[0].Text := Language.Translate('SONG_MENU_JOKER'); @@ -411,168 +409,168 @@ begin SelectsS[0].Visible := False; if (Button[0].Visible) then - Button[0].Text[0].Text := String(Party.Teams[0].Name); + Button[0].Text[0].Text := UTF8String(Party.Teams[0].Name); if (Button[1].Visible) then - Button[1].Text[0].Text := String(Party.Teams[1].Name); + Button[1].Text[0].Text := UTF8String(Party.Teams[1].Name); if (Button[2].Visible) then - Button[2].Text[0].Text := String(Party.Teams[2].Name); + Button[2].Text[0].Text := UTF8String(Party.Teams[2].Name); Button[3].Text[0].Text := Language.Translate('SONG_MENU_CANCEL'); - //Set right Interaction + // set right interaction if (not Button[0].Visible) then begin if (not Button[1].Visible) then begin if (not Button[2].Visible) then - begin - Interaction := 4; - end - else Interaction := 2; + Interaction := 4 + else + Interaction := 2; end - else Interaction := 1; + else + Interaction := 1; end; - + end; end; end; procedure TScreenSongMenu.HandleReturn; begin - Case CurMenu of + case CurMenu of SM_Main: begin - Case Interaction of - 0: //Button 1 + case Interaction of + 0: // button 1 begin ScreenSong.StartSong; - Visible := False; + Visible := false; end; - 1: //Button 2 + 1: // button 2 begin - //Select New Players then Sing: + // select new players then sing: ScreenSong.SelectPlayers; - Visible := False; + Visible := false; end; - 2: //Button 3 + 2: // button 3 begin - //Show add to Playlist Menu + // show add to playlist menu MenuShow(SM_Playlist_Add); end; - 3: //SelectSlide 3 + 3: // selectslide 3 begin //Dummy end; - 4: //Button 4 + 4: // button 4 begin ScreenSong.OpenEditor; - Visible := False; + Visible := false; end; end; end; SM_PlayList: begin - Visible := False; - Case Interaction of - 0: //Button 1 + Visible := false; + case Interaction of + 0: // button 1 begin ScreenSong.StartSong; - Visible := False; + Visible := false; end; - 1: //Button 2 + 1: // button 2 begin - //Select New Players then Sing: + // select new players then sing: ScreenSong.SelectPlayers; - Visible := False; + Visible := false; end; - 2: //Button 3 + 2: // button 3 begin - //Show add to Playlist Menu + // show add to playlist menu MenuShow(SM_Playlist_DelItem); end; - 3: //SelectSlide 3 + 3: // selectslide 3 begin - //Dummy + // dummy end; - 4: //Button 4 + 4: // button 4 begin ScreenSong.OpenEditor; - Visible := False; + Visible := false; end; end; end; SM_Playlist_Add: begin - Case Interaction of - 0: //Button 1 + case Interaction of + 0: // button 1 begin MenuShow(SM_Playlist_New); end; - 3: //SelectSlide 3 + 3: // selectslide 3 begin - //Dummy + // dummy end; - 4: //Button 4 + 4: // button 4 begin PlaylistMan.AddItem(ScreenSong.Interaction, SelectValue); - Visible := False; + Visible := false; end; end; end; SM_Playlist_New: begin - Case Interaction of - 0: //Button 1 + case Interaction of + 0: // button 1 begin - //Nothing, Button for Entering Name + // nothing, button for entering name end; - 2: //Button 3 + 2: // button 3 begin - //Create Playlist and Add Song + // create playlist and add song PlaylistMan.AddItem( ScreenSong.Interaction, PlaylistMan.AddPlaylist(Button[0].Text[0].Text)); - Visible := False; + Visible := false; end; - 3: //SelectSlide 3 + 3: // selectslide 3 begin - //Cancel -> Go back to Add screen + // cancel -> go back to add screen MenuShow(SM_Playlist_Add); end; - 4: //Button 4 + 4: // button 4 begin - Visible := False; + Visible := false; end; end; end; SM_Playlist_DelItem: begin - Visible := False; - Case Interaction of - 0: //Button 1 + Visible := false; + case Interaction of + 0: // button 1 begin - //Delete + // delete PlayListMan.DelItem(PlayListMan.GetIndexbySongID(ScreenSong.Interaction)); - Visible := False; + Visible := false; end; - 4: //Button 4 + 4: // button 4 begin MenuShow(SM_Playlist); end; @@ -581,32 +579,32 @@ begin SM_Playlist_Load: begin - Case Interaction of - 0: //Button 1 (Delete Playlist) + case Interaction of + 0: // button 1 (Delete playlist) begin MenuShow(SM_Playlist_Del); end; - 4: //Button 4 + 4: // button 4 begin - //Load Playlist + // load playlist PlaylistMan.SetPlayList(SelectValue); - Visible := False; + Visible := false; end; end; end; SM_Playlist_Del: begin - Visible := False; - Case Interaction of - 0: //Button 1 + Visible := false; + case Interaction of + 0: // button 1 begin - //Delete + // delete PlayListMan.DelPlaylist(PlaylistMan.CurPlayList); - Visible := False; + Visible := false; end; - 4: //Button 4 + 4: // button 4 begin MenuShow(SM_Playlist_Load); end; @@ -615,17 +613,17 @@ begin SM_Party_Main: begin - Case Interaction of - 0: //Button 1 + case Interaction of + 0: // button 1 begin - //Start Singing + // start singing Party.CallAfterSongSelect; - Visible := False; + Visible := false; end; - 4: //Button 4 + 4: // button 4 begin - //Joker + // joker MenuShow(SM_Party_Joker); end; end; @@ -633,29 +631,29 @@ begin SM_Party_Joker: begin - Visible := False; - Case Interaction of - 0: //Button 1 + Visible := false; + case Interaction of + 0: // button 1 begin - //Joker Team 1 + // joker team 1 ScreenSong.DoJoker(0); end; - 1: //Button 2 + 1: // button 2 begin - //Joker Team 2 + // joker team 2 ScreenSong.DoJoker(1); end; - 2: //Button 3 + 2: // button 3 begin - //Joker Team 3 + // joker team 3 ScreenSong.DoJoker(2); end; - 4: //Button 4 + 4: // button 4 begin - //Cancel... (Fo back to old Menu) + // cancel... (go back to old menu) MenuShow(SM_Party_Main); end; end; @@ -664,4 +662,3 @@ begin end; end. - diff --git a/Lua/src/screens/UScreenStatDetail.pas b/Lua/src/screens/UScreenStatDetail.pas index 20b89b33..1638cd85 100644 --- a/Lua/src/screens/UScreenStatDetail.pas +++ b/Lua/src/screens/UScreenStatDetail.pas @@ -47,40 +47,40 @@ type TScreenStatDetail = class(TMenu) public Typ: TStatType; - Page: Cardinal; - Count: Byte; - Reversed: Boolean; - - TotEntrys: Cardinal; - TotPages: Cardinal; + Page: cardinal; + Count: byte; + Reversed: boolean; + TotEntrys: cardinal; + TotPages: cardinal; constructor Create; override; - function ParseInput(PressedKey: Cardinal; CharCode: WideChar; PressedDown: Boolean): Boolean; override; - procedure onShow; override; + function ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; override; + procedure OnShow; override; procedure SetAnimationProgress(Progress: real); override; procedure SetTitle; - Procedure SetPage(NewPage: Cardinal); + Procedure SetPage(NewPage: cardinal); end; implementation uses - UGraphic, - ULanguage, Math, Classes, - ULog; + UGraphic, + ULanguage, + ULog, + UUnicodeUtils; -function TScreenStatDetail.ParseInput(PressedKey: Cardinal; CharCode: WideChar; PressedDown: Boolean): Boolean; +function TScreenStatDetail.ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; begin Result := true; - If (PressedDown) Then + if (PressedDown) then begin // Key Down // check normal keys - case WideCharUpperCase(CharCode)[1] of - 'Q': + case UCS4UpperCase(CharCode) of + Ord('Q'): begin Result := false; Exit; @@ -97,24 +97,28 @@ begin end; SDLK_RETURN: begin - if Interaction = 0 then begin + if Interaction = 0 then + begin //Next Page SetPage(Page+1); end; - if Interaction = 1 then begin + if Interaction = 1 then + begin //Previous Page if (Page > 0) then SetPage(Page-1); end; - if Interaction = 2 then begin + if Interaction = 2 then + begin //Reverse Order Reversed := not Reversed; SetPage(Page); end; - if Interaction = 3 then begin + if Interaction = 3 then + begin AudioPlayback.PlaySound(SoundLib.Back); FadeTo(@ScreenStatMain); end; @@ -175,7 +179,7 @@ begin Typ := TStatType(0); end; -procedure TScreenStatDetail.onShow; +procedure TScreenStatDetail.OnShow; begin inherited; @@ -187,7 +191,7 @@ begin SetTitle; //Show First Page - Reversed := False; + Reversed := false; SetPage(0); end; @@ -199,12 +203,12 @@ begin Text[Count].Text := Theme.StatDetail.Description[Ord(Typ)]; end; -procedure TScreenStatDetail.SetPage(NewPage: Cardinal); +procedure TScreenStatDetail.SetPage(NewPage: cardinal); var StatList: TList; - I: Integer; - FormatStr: String; - PerPage: Byte; + I: integer; + FormatStr: string; + PerPage: byte; begin // fetch statistics StatList := Database.GetStats(Typ, Count, NewPage, Reversed); @@ -230,7 +234,7 @@ begin if (Score > 0) then begin Text[I].Text := Format(FormatStr, - [Singer, Score, Theme.ILevel[Difficulty], SongArtist, SongTitle]); + [Singer, Score, Theme.ILevel[Difficulty], SongArtist, SongTitle, Date]); end; end; end; @@ -288,9 +292,9 @@ begin Database.FreeStats(StatList); end; - procedure TScreenStatDetail.SetAnimationProgress(Progress: real); -var I: Integer; +var + I: integer; begin for I := 0 to High(Button) do Button[I].Texture.ScaleW := Progress; diff --git a/Lua/src/screens/UScreenStatMain.pas b/Lua/src/screens/UScreenStatMain.pas index a6f67cab..204f40cd 100644 --- a/Lua/src/screens/UScreenStatMain.pas +++ b/Lua/src/screens/UScreenStatMain.pas @@ -46,15 +46,15 @@ type TScreenStatMain = class(TMenu) private //Some Stat Value that don't need to be calculated 2 times - SongsWithVid: Cardinal; - function FormatOverviewIntro(FormatStr: string): string; - function FormatSongOverview(FormatStr: string): string; - function FormatPlayerOverview(FormatStr: string): string; + SongsWithVid: cardinal; + function FormatOverviewIntro(FormatStr: UTF8String): UTF8String; + function FormatSongOverview(FormatStr: UTF8String): UTF8String; + function FormatPlayerOverview(FormatStr: UTF8String): UTF8String; public TextOverview: integer; constructor Create; override; - function ParseInput(PressedKey: Cardinal; CharCode: WideChar; PressedDown: Boolean): Boolean; override; - procedure onShow; override; + function ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; override; + procedure OnShow; override; procedure SetAnimationProgress(Progress: real); override; procedure SetOverview; @@ -62,28 +62,25 @@ type implementation -uses UGraphic, - UDataBase, - USongs, - USong, - ULanguage, - UCommon, - Classes, - {$IFDEF win32} - windows, - {$ELSE} - sysconst, - {$ENDIF} - ULog; - -function TScreenStatMain.ParseInput(PressedKey: Cardinal; CharCode: WideChar; PressedDown: Boolean): Boolean; +uses + UGraphic, + UDataBase, + USongs, + USong, + ULanguage, + UCommon, + Classes, + ULog, + UUnicodeUtils; + +function TScreenStatMain.ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; begin Result := true; - If (PressedDown) Then + if (PressedDown) then begin // Key Down // check normal keys - case WideCharUpperCase(CharCode)[1] of - 'Q': + case UCS4UpperCase(CharCode) of + Ord('Q'): begin Result := false; Exit; @@ -168,12 +165,12 @@ begin //Set Songs with Vid SongsWithVid := 0; - For I := 0 to Songs.SongList.Count -1 do - if (TSong(Songs.SongList[I]).Video <> '') then + for I := 0 to Songs.SongList.Count -1 do + if (TSong(Songs.SongList[I]).Video.IsSet) then Inc(SongsWithVid); end; -procedure TScreenStatMain.onShow; +procedure TScreenStatMain.OnShow; begin inherited; @@ -181,9 +178,9 @@ begin SetOverview; end; -function TScreenStatMain.FormatOverviewIntro(FormatStr: string): string; +function TScreenStatMain.FormatOverviewIntro(FormatStr: UTF8String): UTF8String; var - Year, Month, Day: Word; + Year, Month, Day: word; begin {Format: %0:d Ultrastar Version @@ -202,10 +199,10 @@ begin end; end; -function TScreenStatMain.FormatSongOverview(FormatStr: string): string; +function TScreenStatMain.FormatSongOverview(FormatStr: UTF8String): UTF8String; var - CntSongs, CntSungSongs, CntVidSongs: Integer; - MostPopSongArtist, MostPopSongTitle: String; + CntSongs, CntSungSongs, CntVidSongs: integer; + MostPopSongArtist, MostPopSongTitle: UTF8String; StatList: TList; MostSungSong: TStatResultMostSungSong; begin @@ -220,7 +217,7 @@ begin CntSungSongs := Database.GetTotalEntrys(stMostSungSong); CntVidSongs := SongsWithVid; - StatList := Database.GetStats(stMostSungSong, 1, 0, False); + StatList := Database.GetStats(stMostSungSong, 1, 0, false); if ((StatList <> nil) and (StatList.Count > 0)) then begin MostSungSong := StatList[0]; @@ -246,13 +243,13 @@ begin end; end; -function TScreenStatMain.FormatPlayerOverview(FormatStr: string): string; +function TScreenStatMain.FormatPlayerOverview(FormatStr: UTF8String): UTF8String; var - CntPlayers: Integer; + CntPlayers: integer; BestScoreStat: TStatResultBestScores; BestSingerStat: TStatResultBestSingers; - BestPlayer, BestScorePlayer: String; - BestPlayerScore, BestScore: Integer; + BestPlayer, BestScorePlayer: UTF8String; + BestPlayerScore, BestScore: integer; SingerStats, ScoreStats: TList; begin {Format: @@ -264,7 +261,7 @@ begin CntPlayers := Database.GetTotalEntrys(stBestSingers); - SingerStats := Database.GetStats(stBestSingers, 1, 0, False); + SingerStats := Database.GetStats(stBestSingers, 1, 0, false); if ((SingerStats <> nil) and (SingerStats.Count > 0)) then begin BestSingerStat := SingerStats[0]; @@ -278,7 +275,7 @@ begin end; Database.FreeStats(SingerStats); - ScoreStats := Database.GetStats(stBestScores, 1, 0, False); + ScoreStats := Database.GetStats(stBestScores, 1, 0, false); if ((ScoreStats <> nil) and (ScoreStats.Count > 0)) then begin BestScoreStat := ScoreStats[0]; @@ -306,7 +303,7 @@ end; procedure TScreenStatMain.SetOverview; var - Overview: String; + Overview: UTF8String; begin // Format overview Overview := FormatOverviewIntro(Language.Translate('STAT_OVERVIEW_INTRO')) + '\n \n' + @@ -315,11 +312,11 @@ begin Text[0].Text := Overview; end; - procedure TScreenStatMain.SetAnimationProgress(Progress: real); -var I: Integer; +var + I: integer; begin - For I := 0 to high(Button) do + for I := 0 to high(Button) do Button[I].Texture.ScaleW := Progress; end; diff --git a/Lua/src/screens/UScreenTop5.pas b/Lua/src/screens/UScreenTop5.pas index 59f5972b..2ddff713 100644 --- a/Lua/src/screens/UScreenTop5.pas +++ b/Lua/src/screens/UScreenTop5.pas @@ -34,54 +34,101 @@ interface {$I switches.inc} uses - UMenu, SDL, SysUtils, UDisplay, UMusic, USongs, UThemes; + SysUtils, + SDL, + UDisplay, + UMenu, + UMusic, + USongs, + UThemes; type TScreenTop5 = class(TMenu) public - TextLevel: integer; - TextArtistTitle: integer; + TextLevel: integer; + TextArtistTitle: integer; + DifficultyShow: integer; - StaticNumber: array[1..5] of integer; - TextNumber: array[1..5] of integer; - TextName: array[1..5] of integer; - TextScore: array[1..5] of integer; + StaticNumber: array[1..5] of integer; + TextNumber: array[1..5] of integer; + TextName: array[1..5] of integer; + TextScore: array[1..5] of integer; + TextDate: array[1..5] of integer; + + Fadeout: boolean; - Fadeout: boolean; constructor Create; override; - function ParseInput(PressedKey: Cardinal; CharCode: WideChar; PressedDown: Boolean): Boolean; override; - procedure onShow; override; + function ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; override; + function ParseMouse(MouseButton: integer; BtnDown: boolean; X, Y: integer): boolean; override; + procedure OnShow; override; + procedure DrawScores(difficulty: integer); function Draw: boolean; override; end; implementation -uses UGraphic, UDataBase, UMain, UIni; - -function TScreenTop5.ParseInput(PressedKey: Cardinal; CharCode: WideChar; PressedDown: Boolean): Boolean; +uses + UDataBase, + UGraphic, + UMain, + UIni, + UNote, + UUnicodeUtils; + +function TScreenTop5.ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; begin Result := true; - If (PressedDown) Then begin + if PressedDown then + begin // check normal keys - case WideCharUpperCase(CharCode)[1] of - 'Q': + case UCS4UpperCase(CharCode) of + Ord('Q'): begin Result := false; Exit; end; end; - + // check special keys case PressedKey of SDLK_ESCAPE, SDLK_BACKSPACE, SDLK_RETURN: begin - if (not Fadeout) then begin + if (not Fadeout) then + begin FadeTo(@ScreenSong); Fadeout := true; end; end; + SDLK_RIGHT: + begin + inc(DifficultyShow); + if (DifficultyShow>2) then + DifficultyShow:=0; + DrawScores(DifficultyShow); + end; + SDLK_LEFT: + begin + dec(DifficultyShow); + if (DifficultyShow<0) then + DifficultyShow:=2; + DrawScores(DifficultyShow); + end; + SDLK_UP: + begin + inc(DifficultyShow); + if (DifficultyShow>2) then + DifficultyShow:=0; + DrawScores(DifficultyShow); + end; + SDLK_DOWN: + begin + dec(DifficultyShow); + if (DifficultyShow<0) then + DifficultyShow:=2; + DrawScores(DifficultyShow); + end; SDLK_SYSREQ: begin Display.SaveScreenShot; @@ -90,91 +137,150 @@ begin end; end; +function TScreenTop5.ParseMouse(MouseButton: integer; + BtnDown: boolean; + X, Y: integer): boolean; +begin + Result := true; + if (MouseButton = SDL_BUTTON_LEFT) and BtnDown then + //left-click anywhere sends return + ParseInput(SDLK_RETURN, 0, true); +end; + constructor TScreenTop5.Create; var - I: integer; + I: integer; begin inherited Create; LoadFromTheme(Theme.Top5); - - TextLevel := AddText(Theme.Top5.TextLevel); + TextLevel := AddText(Theme.Top5.TextLevel); TextArtistTitle := AddText(Theme.Top5.TextArtistTitle); for I := 0 to 4 do - StaticNumber[I+1] := AddStatic( Theme.Top5.StaticNumber[I] ); - - for I := 0 to 4 do - TextNumber[I+1] := AddText(Theme.Top5.TextNumber[I]); - for I := 0 to 4 do - TextName[I+1] := AddText(Theme.Top5.TextName[I]); - for I := 0 to 4 do - TextScore[I+1] := AddText(Theme.Top5.TextScore[I]); + begin + StaticNumber[I+1] := AddStatic(Theme.Top5.StaticNumber[I]); + TextNumber[I+1] := AddText (Theme.Top5.TextNumber[I]); + TextName[I+1] := AddText (Theme.Top5.TextName[I]); + TextScore[I+1] := AddText (Theme.Top5.TextScore[I]); + TextDate[I+1] := AddText (Theme.Top5.TextDate[I]); + end; end; -procedure TScreenTop5.onShow; +procedure TScreenTop5.OnShow; var - I: integer; - PMax: integer; + I: integer; + PMax: integer; + sung: boolean; //score added? otherwise in wasn't sung! begin inherited; + sung := false; Fadeout := false; + DifficultyShow := Ini.Difficulty; //ReadScore(CurrentSong); PMax := Ini.Players; - if PMax = 4 then PMax := 5; + if PMax = 4 then + PMax := 5; for I := 0 to PMax do - DataBase.AddScore(CurrentSong, Ini.Difficulty, Ini.Name[I], Round(Player[I].ScoreTotalInt)); + begin + if (Round(Player[I].ScoreTotalInt) > 0) and (ScreenSing.SungToEnd) then + begin + DataBase.AddScore(CurrentSong, Ini.Difficulty, Ini.Name[I], Round(Player[I].ScoreTotalInt)); + sung:=true; + end; + end; - DataBase.WriteScore(CurrentSong); + if sung then + DataBase.WriteScore(CurrentSong); DataBase.ReadScore(CurrentSong); Text[TextArtistTitle].Text := CurrentSong.Artist + ' - ' + CurrentSong.Title; - for I := 1 to Length(CurrentSong.Score[Ini.Difficulty]) do begin + for I := 1 to Length(CurrentSong.Score[Ini.Difficulty]) do + begin Static[StaticNumber[I]].Visible := true; Text[TextNumber[I]].Visible := true; Text[TextName[I]].Visible := true; Text[TextScore[I]].Visible := true; + Text[TextDate[I]].Visible := true; Text[TextName[I]].Text := CurrentSong.Score[Ini.Difficulty, I-1].Name; Text[TextScore[I]].Text := IntToStr(CurrentSong.Score[Ini.Difficulty, I-1].Score); + Text[TextDate[I]].Text := CurrentSong.Score[Ini.Difficulty, I-1].Date; end; - for I := Length(CurrentSong.Score[Ini.Difficulty])+1 to 5 do begin + for I := Length(CurrentSong.Score[Ini.Difficulty]) + 1 to 5 do + begin Static[StaticNumber[I]].Visible := false; Text[TextNumber[I]].Visible := false; Text[TextName[I]].Visible := false; Text[TextScore[I]].Visible := false; + Text[TextDate[I]].Visible := false; end; Text[TextLevel].Text := IDifficulty[Ini.Difficulty]; end; +procedure TScreenTop5.DrawScores(difficulty: integer); +var + I: integer; +begin + for I := 1 to Length(CurrentSong.Score[difficulty]) do + begin + Static[StaticNumber[I]].Visible := true; + Text[TextNumber[I]].Visible := true; + Text[TextName[I]].Visible := true; + Text[TextScore[I]].Visible := true; + Text[TextDate[I]].Visible := true; + + Text[TextName[I]].Text := CurrentSong.Score[difficulty, I-1].Name; + Text[TextScore[I]].Text := IntToStr(CurrentSong.Score[difficulty, I-1].Score); + Text[TextDate[I]].Text := CurrentSong.Score[difficulty, I-1].Date; + end; + + for I := Length(CurrentSong.Score[difficulty]) + 1 to 5 do + begin + Static[StaticNumber[I]].Visible := false; + Text[TextNumber[I]].Visible := false; + Text[TextName[I]].Visible := false; + Text[TextScore[I]].Visible := false; + Text[TextDate[I]].Visible := false; + end; + + Text[TextLevel].Text := IDifficulty[difficulty]; +end; + function TScreenTop5.Draw: boolean; //var -{ Min: real; - Max: real; - Factor: real; - Factor2: real; - - Item: integer; - P: integer; - C: integer;} +{ + Min: real; + Max: real; + Factor: real; + Factor2: real; + + Item: integer; + P: integer; + C: integer; +} begin // Singstar - let it be...... with 6 statics -(* if PlayersPlay = 6 then begin - for Item := 4 to 6 do begin +(* + if PlayersPlay = 6 then + begin + for Item := 4 to 6 do + begin if ScreenAct = 1 then P := Item-4; if ScreenAct = 2 then P := Item-1; FillPlayer(Item, P); - -{ if ScreenAct = 1 then begin +{ + if ScreenAct = 1 then + begin LoadColor( Static[StaticBoxLightest[Item]].Texture.ColR, Static[StaticBoxLightest[Item]].Texture.ColG, @@ -182,16 +288,18 @@ begin 'P1Dark'); end; - if ScreenAct = 2 then begin + if ScreenAct = 2 then + begin LoadColor( Static[StaticBoxLightest[Item]].Texture.ColR, Static[StaticBoxLightest[Item]].Texture.ColG, Static[StaticBoxLightest[Item]].Texture.ColB, 'P4Dark'); - end; } - + end; +} end; - end; *) + end; +*) Result := inherited Draw; end; diff --git a/Lua/src/screens/UScreenWelcome.pas b/Lua/src/screens/UScreenWelcome.pas index 485ebedb..4b463613 100644 --- a/Lua/src/screens/UScreenWelcome.pas +++ b/Lua/src/screens/UScreenWelcome.pas @@ -34,7 +34,10 @@ interface {$I switches.inc} uses - UMenu, SDL, SysUtils, UThemes; + UMenu, + SDL, + SysUtils, + UThemes; type TScreenWelcome = class(TMenu) @@ -42,24 +45,29 @@ type Animation: real; Fadeout: boolean; constructor Create; override; - function ParseInput(PressedKey: Cardinal; CharCode: WideChar; PressedDown: Boolean): Boolean; override; + function ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; override; function Draw: boolean; override; - procedure onShow; override; + procedure OnShow; override; end; implementation -uses UGraphic, UTime, USkins, UTexture; +uses + UGraphic, + UTime, + USkins, + UTexture; -function TScreenWelcome.ParseInput(PressedKey: Cardinal; CharCode: WideChar; PressedDown: Boolean): Boolean; +function TScreenWelcome.ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; begin Result := true; - If (PressedDown) Then begin + if (PressedDown) then + begin case PressedKey of SDLK_ESCAPE, SDLK_BACKSPACE : begin - Result := False; + Result := false; end; SDLK_RETURN: begin @@ -83,7 +91,7 @@ begin Fadeout := false; end; -procedure TScreenWelcome.onShow; +procedure TScreenWelcome.OnShow; begin inherited; @@ -102,12 +110,14 @@ begin // draw nothing Min := 0; Max := 1000; - if (Animation >= Min) and (Animation < Max) then begin + if (Animation >= Min) and (Animation < Max) then + begin end; // popup Min := 1000; Max := 1120; - if (Animation >= Min) and (Animation < Max) then begin + if (Animation >= Min) and (Animation < Max) then + begin Factor := (Animation - Min) / (Max - Min); Static[0].Texture.X := 600; Static[0].Texture.Y := 600 - Factor * 230; @@ -117,7 +127,8 @@ begin // bounce Min := 1120; Max := 1200; - if (Animation >= Min) and (Animation < Max) then begin + if (Animation >= Min) and (Animation < Max) then + begin Factor := (Animation - Min) / (Max - Min); Static[0].Texture.Y := 370 + Factor * 50; Static[0].Texture.H := 230 - Factor * 50; @@ -125,14 +136,15 @@ begin // run Min := 1500; Max := 3500; - if (Animation >= Min) and (Animation < Max) then begin + if (Animation >= Min) and (Animation < Max) then + begin Factor := (Animation - Min) / (Max - Min); Static[0].Texture.X := 600 - Factor * 1400; Static[0].Texture.H := 180; - - for Count := 1 to 5 do begin + for Count := 1 to 5 do + begin Static[Count].Texture.X := 770 - Factor * 1400; Static[Count].Texture.W := 150 + Factor * 200; Static[Count].Texture.Alpha := Factor * 0.5; @@ -140,7 +152,8 @@ begin end; Min := 3500; - if (Animation >= Min) and (not Fadeout) then begin + if (Animation >= Min) and (not Fadeout) then + begin FadeTo(@ScreenMain); Fadeout := true; end; diff --git a/Lua/src/switches.inc b/Lua/src/switches.inc index 215fe239..55d8e619 100644 --- a/Lua/src/switches.inc +++ b/Lua/src/switches.inc @@ -15,14 +15,30 @@ {$ELSE} {$DEFINE Delphi} - // Delphi version numbers (ignore versions released before Delphi 6 as they miss the $IF directive): - // Delphi 6 (VER140), Delphi 7 (VER150), Delphi 8 (VER160) - // Delphi 9/2005 (VER170), Delphi 10/2006 (VER180) + // Delphi version numbers (ignore Delphi < 7 and Delphi 8 (VER160)) - // the inline-procedure directive was introduced with Delphi 2005 - {$IF not (Defined(VER140) or Defined(VER150) or Defined(VER160))} + {$IFDEF VER180} // Delphi 2006 (=10) + {$DEFINE DELPHI_10} + {$DEFINE DELPHI_7_UP} + {$DEFINE DELPHI_9_UP} + {$DEFINE DELPHI_10_UP} + {$ENDIF} + + {$IFDEF VER170} // Delphi 2005 (=9) + {$DEFINE DELPHI_9} + {$DEFINE DELPHI_7_UP} + {$DEFINE DELPHI_9_UP} + {$ENDIF} + + {$IFDEF VER150} // Delphi 7 + {$DEFINE DELPHI_7} + {$DEFINE DELPHI_7_UP} + {$ENDIF} + + // inline directive introduced with Delphi 2005 + {$IFDEF DELPHI_9_UP} {$DEFINE HasInline} - {$IFEND} + {$ENDIF} {$ENDIF} @@ -65,8 +81,6 @@ {$DEFINE CONSOLE} {$IFEND} -{.$DEFINE UseFreetype} - // audio config {$IF Defined(HaveBASS)} {$DEFINE UseBASSPlayback} @@ -114,4 +128,4 @@ {$DEFINE UsePortaudio} {$IFEND} -{$ENDIF PASDOC}
\ No newline at end of file +{$ENDIF PASDOC} diff --git a/Lua/src/ultrastardx.dpr b/Lua/src/ultrastardx.dpr index cedaefb1..fdfb8100 100644 --- a/Lua/src/ultrastardx.dpr +++ b/Lua/src/ultrastardx.dpr @@ -50,11 +50,7 @@ uses {$IFDEF Unix} cthreads, // THIS MUST be the first used unit in FPC if Threads are used!! // (see http://wiki.lazarus.freepascal.org/Multithreaded_Application_Tutorial) - // cwstring crashes in FPC 2.2.2 so do not use the cwstring stuff - {.$IFNDEF DARWIN} - {$IFDEF NOIGNORE} - cwstring, // Enable Unicode support. MacOSX misses some references to iconv. - {$ENDIF} + cwstring, // Enable Unicode support {$ENDIF} {$IFNDEF FPC} @@ -71,16 +67,12 @@ uses sdl in 'lib\JEDI-SDL\SDL\Pas\sdl.pas', sdl_image in 'lib\JEDI-SDL\SDL_Image\Pas\sdl_image.pas', sdlutils in 'lib\JEDI-SDL\SDL\Pas\sdlutils.pas', + sdlstreams in 'lib\JEDI-SDL\SDL\Pas\sdlstreams.pas', UMediaCore_SDL in 'media\UMediaCore_SDL.pas', zlib in 'lib\zlib\zlib.pas', png in 'lib\libpng\png.pas', - - {$IFDEF UseFreetype} freetype in 'lib\freetype\freetype.pas', - UFont in 'base\UFont.pas', - UTextEncoding in 'base\UTextEncoding.pas', - {$ENDIF} {$IFDEF UseBass} bass in 'lib\bass\delphi\bass.pas', @@ -136,14 +128,28 @@ uses {$IFDEF DARWIN} PseudoThread in 'macosx\PseudoThread.pas', {$ENDIF} - + SQLiteTable3 in 'lib\SQLite\SQLiteTable3.pas', SQLite3 in 'lib\SQLite\SQLite3.pas', + pcre in 'lib\pcre\pcre.pas', + + {$IFDEF MSWINDOWS} + // TntUnicodeControls + TntSystem in 'lib\TntUnicodeControls\TntSystem.pas', + TntSysUtils in 'lib\TntUnicodeControls\TntSysUtils.pas', + TntWindows in 'lib\TntUnicodeControls\TntWindows.pas', + TntWideStrUtils in 'lib\TntUnicodeControls\TntWideStrUtils.pas', + TntClasses in 'lib\TntUnicodeControls\TntClasses.pas', + TntFormatStrUtils in 'lib\TntUnicodeControls\TntFormatStrUtils.pas', + {$IFNDEF DELPHI_10_UP} // WideStrings for FPC and Delphi < 2006 + TntWideStrings in 'lib\TntUnicodeControls\TntWideStrings.pas', + {$ENDIF} + {$ENDIF} + //------------------------------ //Includes - Lua Support //------------------------------ - ULua in 'lib\Lua\ULua.pas', ULuaUtils in 'lua\ULuaUtils.pas', ULuaGl in 'lua\ULuaGl.pas', @@ -154,7 +160,7 @@ uses ULuaCore in 'lua\ULuaCore.pas', ULuaUsdx in 'lua\ULuaUsdx.pas', ULuaParty in 'lua\ULuaParty.pas', - ULuaScreenSing in 'lua\ULuaScreenSing.pas', + ULuaScreenSing in 'lua\ULuaScreenSing.pas', //------------------------------ //Includes - Menu System @@ -190,7 +196,6 @@ uses UDraw in 'base\UDraw.pas', URecord in 'base\URecord.pas', UTime in 'base\UTime.pas', - TextGL in 'base\TextGL.pas', USong in 'base\USong.pas', UXMLSong in 'base\UXMLSong.pas', USongs in 'base\USongs.pas', @@ -213,26 +218,21 @@ uses URingBuffer in 'base\URingBuffer.pas', USingScores in 'base\USingScores.pas', USingNotes in 'base\USingNotes.pas', + UPathUtils in 'base\UPathUtils.pas', + UNote in 'base\UNote.pas', + UBeatTimer in 'base\UBeatTimer.pas', + + TextGL in 'base\TextGL.pas', + UUnicodeUtils in 'base\UUnicodeUtils.pas', + UFont in 'base\UFont.pas', + UTextEncoding in 'base\UTextEncoding.pas', + + UPath in 'base\UPath.pas', + UFilesystem in 'base\UFilesystem.pas', //------------------------------ //Includes - Plugin Support //------------------------------ - {UPluginDefines in 'pluginsupport\UPluginDefines.pas', - UPartyDefines in 'pluginsupport\UPartyDefines.pas', - - UPartyMode in 'pluginsupport\UPartyMode.pas', - UPartyManager in 'pluginsupport\UPartyManager.pas', - UPartyModePlugin in 'pluginsupport\UPartyModePlugin.pas', - UPluginLoader in 'pluginsupport\UPluginLoader.pas', } - - UModules in 'base\UModules.pas', //List of Modules to Load - UHooks in 'base\UHooks.pas', //Hook Managing - UServices in 'base\UServices.pas', //Service Managing - UCore in 'base\UCore.pas', //Core, Maybe remove this - UCoreModule in 'base\UCoreModule.pas', //^ - UPluginInterface in 'base\UPluginInterface.pas', //Interface offered by Core to Plugins - UPluginLoader in 'base\UPluginLoader.pas', //New Plugin Loader Module - UParty in 'base\UParty.pas', // TODO: rewrite Party Manager as Module, reomplent ability to offer party Mody by Plugin //------------------------------ @@ -256,7 +256,7 @@ uses UAudioPlaybackBase in 'media\UAudioPlaybackBase.pas', {$IF Defined(UsePortaudioPlayback) or Defined(UseSDLPlayback)} UFFT in 'lib\fft\UFFT.pas', - UAudioPlayback_Softmixer in 'media\UAudioPlayback_SoftMixer.pas', + UAudioPlayback_SoftMixer in 'media\UAudioPlayback_SoftMixer.pas', {$IFEND} UAudioConverter in 'media\UAudioConverter.pas', @@ -344,8 +344,6 @@ uses //Includes - Modi SDK //------------------------------ ModiSDK in '..\plugins\SDK\ModiSDK.pas', //Old SDK, will be deleted soon - UPluginDefs in '..\plugins\SDK\UPluginDefs.pas', //New SDK, not only Modis - UPartyDefs in '..\plugins\SDK\UPartyDefs.pas', //Headers to register Party Modes SysUtils; |