From 21c1082f916cc9a4d7be625c132e02b1fc1d8012 Mon Sep 17 00:00:00 2001 From: tobigun Date: Thu, 23 Jul 2009 18:09:11 +0000 Subject: - IPath integration - BASS is now unicode compatible git-svn-id: svn://svn.code.sf.net/p/ultrastardx/svn/branches/experimental@1875 b956fd51-792f-4845-bead-9b4dfca2ff2c --- unicode/src/base/TextGL.pas | 22 +- unicode/src/base/UCatCovers.pas | 114 ++++--- unicode/src/base/UCommandLine.pas | 25 +- unicode/src/base/UCommon.pas | 82 ++--- unicode/src/base/UConfig.pas | 8 +- unicode/src/base/UCovers.pas | 51 +-- unicode/src/base/UDLLManager.pas | 57 ++-- unicode/src/base/UDataBase.pas | 23 +- unicode/src/base/UFilename.pas | 464 -------------------------- unicode/src/base/UFiles.pas | 142 ++++---- unicode/src/base/UFilesystem.pas | 2 +- unicode/src/base/UFont.pas | 47 +-- unicode/src/base/UGraphic.pas | 22 +- unicode/src/base/UImage.pas | 63 ++-- unicode/src/base/UIni.pas | 179 +++++----- unicode/src/base/ULanguage.pas | 31 +- unicode/src/base/ULog.pas | 22 +- unicode/src/base/UMain.pas | 8 +- unicode/src/base/UMusic.pas | 53 +-- unicode/src/base/UPath.pas | 260 ++++++++++----- unicode/src/base/UPathUtils.pas | 120 +++---- unicode/src/base/UPlatform.pas | 95 +----- unicode/src/base/UPlatformLinux.pas | 98 ++---- unicode/src/base/UPlatformMacOSX.pas | 92 ++---- unicode/src/base/UPlatformWindows.pas | 125 +------ unicode/src/base/UPlaylist.pas | 250 +++++++------- unicode/src/base/USkins.pas | 77 ++--- unicode/src/base/USong.pas | 291 +++++++++------- unicode/src/base/USongs.pas | 68 ++-- unicode/src/base/UTextEncoding.pas | 474 +++++++++++++-------------- unicode/src/base/UTexture.pas | 45 +-- unicode/src/base/UThemes.pas | 41 ++- unicode/src/base/UUnicodeUtils.pas | 10 + unicode/src/base/UXMLSong.pas | 86 +++-- unicode/src/lib/midi/MidiFile.pas | 26 +- unicode/src/media/UAudioCore_Bass.pas | 12 + unicode/src/media/UAudioDecoder_Bass.pas | 21 +- unicode/src/media/UAudioDecoder_FFmpeg.pas | 39 +-- unicode/src/media/UAudioInput_Bass.pas | 5 + unicode/src/media/UAudioPlaybackBase.pas | 23 +- unicode/src/media/UAudioPlayback_Bass.pas | 6 +- unicode/src/media/UMedia_dummy.pas | 15 +- unicode/src/media/UVideo.pas | 19 +- unicode/src/menu/UDisplay.pas | 55 ++-- unicode/src/menu/UMenu.pas | 90 ++--- unicode/src/menu/UMenuBackgroundColor.pas | 144 ++++---- unicode/src/menu/UMenuBackgroundFade.pas | 6 +- unicode/src/menu/UMenuBackgroundNone.pas | 138 ++++---- unicode/src/menu/UMenuBackgroundTexture.pas | 7 +- unicode/src/menu/UMenuBackgroundVideo.pas | 11 +- unicode/src/screens/UScreenCredits.pas | 67 ++-- unicode/src/screens/UScreenEditConvert.pas | 11 +- unicode/src/screens/UScreenEditHeader.pas | 25 +- unicode/src/screens/UScreenEditSub.pas | 24 +- unicode/src/screens/UScreenOpen.pas | 19 +- unicode/src/screens/UScreenOptionsRecord.pas | 4 +- unicode/src/screens/UScreenOptionsThemes.pas | 4 +- unicode/src/screens/UScreenPartyOptions.pas | 26 +- unicode/src/screens/UScreenPartyScore.pas | 12 +- unicode/src/screens/UScreenPopup.pas | 8 +- unicode/src/screens/UScreenSing.pas | 30 +- unicode/src/screens/UScreenSingModi.pas | 22 +- unicode/src/screens/UScreenSong.pas | 27 +- unicode/src/screens/UScreenSongMenu.pas | 2 +- unicode/src/screens/UScreenStatMain.pas | 2 +- unicode/src/ultrastardx.dpr | 4 +- 66 files changed, 1997 insertions(+), 2454 deletions(-) delete mode 100644 unicode/src/base/UFilename.pas (limited to 'unicode/src') diff --git a/unicode/src/base/TextGL.pas b/unicode/src/base/TextGL.pas index 65d716c2..7fe98d29 100644 --- a/unicode/src/base/TextGL.pas +++ b/unicode/src/base/TextGL.pas @@ -37,9 +37,10 @@ uses gl, glext, SDL, + Classes, UTexture, UFont, - Classes, + UPath, ULog; type @@ -75,26 +76,26 @@ uses UMain, UPathUtils; -function FindFontFile(FontIni: TCustomIniFile; Font: string): string; +function FindFontFile(FontIni: TCustomIniFile; Font: string): IPath; var - Filename: string; + Filename: IPath; begin - Filename := FontIni.ReadString(Font, 'File', ''); - Result := FontPath + Filename; + Filename := Path(FontIni.ReadString(Font, 'File', '')); + Result := FontPath.Append(Filename); // if path does not exist, try as an absolute path - if (not FileExists(Result)) then + if (not Result.IsFile) then Result := Filename; end; procedure BuildFont; var FontIni: TMemIniFile; - FontFile: string; + FontFile: IPath; begin ActFont := 0; SetLength(Fonts, 4); - FontIni := TMemIniFile.Create(FontPath + 'fonts.ini'); + FontIni := TMemIniFile.Create(FontPath.Append('fonts.ini').ToNative); try @@ -117,8 +118,9 @@ begin FontFile := FindFontFile(FontIni, 'Outline2'); Fonts[3].Font := TFTScalableOutlineFont.Create(FontFile, 64, 0.08); - except on E: Exception do - Log.LogCritical(E.Message, 'BuildFont'); + except + on E: Exception do + Log.LogCritical(E.Message, 'BuildFont'); end; // close ini-file diff --git a/unicode/src/base/UCatCovers.pas b/unicode/src/base/UCatCovers.pas index 86f675c5..6e004b22 100644 --- a/unicode/src/base/UCatCovers.pas +++ b/unicode/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,10 @@ uses IniFiles, SysUtils, Classes, - // UFiles, + UFilesystem, ULog, UMain, + UUnicodeUtils, UPathUtils; constructor TCatCovers.Create; @@ -79,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 @@ -106,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) or (I = sTitle2)) and (UTF8Pos('Title', TmpName) <> 0) then + UTF8Delete(TmpName, UTF8Pos('Title', TmpName), 5) + else if (I = sArtist) or (I = sArtist2) 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 @@ -171,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; @@ -188,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/unicode/src/base/UCommandLine.pas b/unicode/src/base/UCommandLine.pas index 281a480d..ac0db2c2 100644 --- a/unicode/src/base/UCommandLine.pas +++ b/unicode/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/unicode/src/base/UCommon.pas b/unicode/src/base/UCommon.pas index d7c36196..c0a98815 100644 --- a/unicode/src/base/UCommon.pas +++ b/unicode/src/base/UCommon.pas @@ -41,7 +41,8 @@ uses {$ENDIF} sdl, UConfig, - ULog; + ULog, + UPath; type TMessageType = (mtInfo, mtError); @@ -65,8 +66,16 @@ procedure RestoreNumericLocale(); function MakeLong(a, b: word): longint; {$ENDIF} -function AdaptFilePaths(const aPath: widestring): widestring; -function FileExistsInsensitive(var FileName: string): boolean; +type + TDirectoryEntry = record + Name: IPath; + IsDirectory: boolean; + IsFile: boolean; + end; + + TDirectoryEntryArray = array of TDirectoryEntry; + +function DirectoryFindFiles(Dir: IPath; Filter: UTF8String; ReturnAllSubDirs: boolean): TDirectoryEntryArray; // A stable alternative to TList.Sort() (use TList.Sort() if applicable, see below) procedure MergeSort(List: TList; CompareFunc: TListSortCompare); @@ -82,6 +91,7 @@ uses {$IFDEF Delphi} Dialogs, {$ENDIF} + UFilesystem, UMain, UUnicodeUtils; @@ -206,12 +216,6 @@ begin exOverflow, exUnderflow, exPrecision]); end; -function AdaptFilePaths(const aPath: WideString): WideString; -begin - result := StringReplaceW(aPath, '\', PathDelim);//, [rfReplaceAll]); -end; - - {$IFNDEF MSWINDOWS} procedure ZeroMemory(Destination: pointer; Length: dword); begin @@ -225,42 +229,44 @@ end; {$ENDIF} -{ TODO: REMOVE } -// Checks if a regular files or directory with the given name exists. -// The comparison is case insensitive. -function FileExistsInsensitive(var FileName: string): boolean; +function DirectoryFindFiles(Dir: IPath; Filter: UTF8String; ReturnAllSubDirs: Boolean): TDirectoryEntryArray; var - FilePath, LocalFileName: string; - SearchInfo: TSearchRec; + i: integer; + Iter: IFileIterator; + FileInfo: TFileInfo; + FileName: IPath; + Attrib: integer; begin -{$IF Defined(Linux) or Defined(FreeBSD)} - // speed up standard case - if FileExists(FileName) then - begin - Result := true; - exit; - end; + i := 0; + Filter := UTF8LowerCase(Filter); - Result := false; - - FilePath := ExtractFilePath(FileName); - if (FindFirst(FilePath + '*', faAnyFile, SearchInfo) = 0) then + // search for all files and directories + Iter := FileSystem.FileFind(Dir.Append('*'), faAnyFile); + while (Iter.HasNext) do begin - LocalFileName := ExtractFileName(FileName); - repeat - if (AnsiSameText(LocalFileName, SearchInfo.Name)) then + FileInfo := Iter.Next; + FileName := FileInfo.Name; + if (not FileName.Equals('.')) and (not FileName.Equals('..')) then + begin + Attrib := Dir.Append(FileName).GetAttr(); + if ReturnAllSubDirs and ((Attrib and faDirectory) <> 0) then begin - FileName := FilePath + SearchInfo.Name; - Result := true; - break; + SetLength(Result, i + 1); + Result[i].Name := FileName; + Result[i].IsDirectory := true; + Result[i].IsFile := false; + i := i + 1; + end + else if (Filter = '') or (Pos(Filter, LowerCase(FileName.ToUTF8)) > 0) then + begin + SetLength(Result, i + 1); + Result[i].Name := FileName; + Result[i].IsDirectory := false; + Result[i].IsFile := true; + i := i + 1; end; - until (FindNext(SearchInfo) <> 0); + end; end; - FindClose(SearchInfo); -{$ELSE} - // Windows and Mac OS X do not have case sensitive file systems - Result := FileExists(FileName); -{$IFEND} end; // +++++++++++++++++++++ helpers for RWOpsFromStream() +++++++++++++++ diff --git a/unicode/src/base/UConfig.pas b/unicode/src/base/UConfig.pas index 1214f36f..f6dc69a5 100644 --- a/unicode/src/base/UConfig.pas +++ b/unicode/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/unicode/src/base/UCovers.pas b/unicode/src/base/UCovers.pas index 8e7934b2..0dbe672a 100644 --- a/unicode/src/base/UCovers.pas +++ b/unicode/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; @@ -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: UTF8String; + Filename: IPath; begin - Filename := 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); @@ -403,7 +404,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 +418,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/unicode/src/base/UDLLManager.pas b/unicode/src/base/UDLLManager.pas index dd05d733..d5bb1480 100644 --- a/unicode/src/base/UDLLManager.pas +++ b/unicode/src/base/UDLLManager.pas @@ -35,7 +35,9 @@ interface uses ModiSDK, - UFiles; + UFiles, + UPath, + UFilesystem; type TDLLMan = class @@ -47,14 +49,14 @@ type 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; + function LoadPluginInfo(const Filename: IPath; No: cardinal): boolean; function LoadPlugin(No: cardinal): boolean; procedure UnLoadPlugin; @@ -107,27 +109,26 @@ end; procedure TDLLMan.GetPluginList; var - SearchRecord: TSearchRec; + Iter: IFileIterator; + FileInfo: TFileInfo; begin - - if FindFirst(PluginPath + '*' + DLLExt, faAnyFile, SearchRecord) = 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(SearchRecord.Name, High(Plugins)) then // loaded succesful - begin - PluginPaths[High(PluginPaths)] := SearchRecord.Name; - end - else // error loading - begin - SetLength(Plugins, Length(Plugins)-1); - SetLength(PluginPaths, Length(Plugins)); - end; - - until FindNext(SearchRecord) <> 0; - FindClose(SearchRecord); + 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; @@ -164,7 +165,7 @@ begin 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; @@ -182,7 +183,7 @@ begin } // load libary - hLibg := LoadLibrary(PChar(PluginPath + Filename)); + hLibg := LoadLibrary(PChar(PluginPath.Append(Filename).ToNative)); // if loaded if (hLibg <> 0) then begin @@ -197,19 +198,19 @@ begin 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'); + Log.LogError('Could not load plugin "' + Filename.ToNative + '": Libary not loaded'); end; function TDLLMan.LoadPlugin(No: cardinal): boolean; begin Result := true; // load libary - hLib := LoadLibrary(PChar(PluginPath + PluginPaths[No])); + hLib := LoadLibrary(PChar(PluginPath.Append(PluginPaths[No]).ToNative)); // if loaded if (hLib <> 0) then begin @@ -226,11 +227,11 @@ begin 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'); + Log.LogError('Could not load plugin "' + PluginPaths[No].ToNative + '": Libary not loaded'); end; procedure TDLLMan.UnLoadPlugin; diff --git a/unicode/src/base/UDataBase.pas b/unicode/src/base/UDataBase.pas index 87e9519c..90cee974 100644 --- a/unicode/src/base/UDataBase.pas +++ b/unicode/src/base/UDataBase.pas @@ -34,10 +34,11 @@ interface {$I switches.inc} uses + Classes, + SQLiteTable3, USongs, USong, - Classes, - SQLiteTable3; + UPath; //-------------------- //DataBaseSystem - Class including all DB Methods @@ -88,16 +89,16 @@ type TDataBaseSystem = class private ScoreDB: TSQLiteDatabase; - fFilename: string; + 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: UTF8String; Score: integer); procedure WriteScore(Song: TSong); @@ -128,19 +129,19 @@ const (** * Opens Database and Create Tables if not Exist *) -procedure TDataBaseSystem.Init(const Filename: string); +procedure TDataBaseSystem.Init(const Filename: IPath); var Version: integer; 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); + ScoreDB := TSQLiteDatabase.Create(Filename.ToUTF8); fFilename := Filename; // Close and delete outdated file @@ -150,10 +151,10 @@ 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); + if (not Filename.DeleteFile()) then + raise Exception.Create('Could not delete ' + Filename.ToNative); // Reopen - ScoreDB := TSQLiteDatabase.Create(Filename); + ScoreDB := TSQLiteDatabase.Create(Filename.ToUTF8); Version := 0; end; diff --git a/unicode/src/base/UFilename.pas b/unicode/src/base/UFilename.pas deleted file mode 100644 index c2ccaec8..00000000 --- a/unicode/src/base/UFilename.pas +++ /dev/null @@ -1,464 +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 UFilename; - -{$IFDEF FPC} - {$MODE Delphi} -{$ENDIF} - -{$I switches.inc} - -interface - -uses - {$IFDEF MSWINDOWS} - TntClasses, - {$ENDIF} - Classes; - -type - TPathEncoding = ( - // pencLossless, - pencSystemUTF8, // use system encoding. Mac: UTF8, Linux: None, Win: UTF8 - // Note: - // - On windows, this format is lossless but is not - // supported direclty (UTF-16 only). - // - This is the best format to store filenames as there is - // no dataloss. - // - Do not use with glPrint() or other display functions - // as the resulting string is neither guaranteed to be - // UTF-8 nor ANSI. - - pencSystemANSI, // use system encoding. Mac: UTF8, Linux: None, Win: ANSI - // Note: - // - On windows, the ANSI format may break filenames - // that are not encoded in the active codepage. - // - Use only if there is no equivalent WideString - // filesystem function. - // - Do not use with glPrint() or other display functions - // as the resulting string is neither guaranteed to be - // UTF-8 nor ANSI. - - // pencPrintable, - pencUTF8 // tries to present the filename as UTF-8 encoded string - // that can be used for printing etc. - // Note: - // - Some characters whose encodings are unknown are - // represented either by '?' or as the representative of - // a different encoding. - // - On linux not all filenames can be represented - // correclty in UTF-8 as it does not have filename - // encodings (though most filenames are stored in UTF-8). - // - Never use the result to store filenames or open files - // as the filenames may break due to conversion. - ); - - IPath = interface; - - {$IFDEF MSWINDOWS} - TUniFileStream = class(TTntFileStream) - {$ELSE} - TUniFileStream = class(TFileStream) - {$ENDIF} - public - constructor Create(const FileName: IPath; Mode: Word); - end; - - TUniMemoryStream = class(TMemoryStream) - public - procedure LoadFromFile(const FileName: IPath); - procedure SaveToFile(const FileName: IPath); - end; - - IPath = interface - function ToString(Encoding: TPathEncoding = pencSystemUTF8): AnsiString; - function ToWideString(): WideString; - - function Adjust(): boolean; - - function Expand(): IPath; - {** File must be closed with FileClose(Handle) after usage } - function CreateFile(): integer; - function CreateDir(): boolean; - {** File must be closed with FileClose(Handle) after usage } - function Open(Mode: LongWord): integer; - {** Stream must be freed with TUniFileStream.Free after usage } - function OpenStream(Mode: Word): TUniFileStream; - function GetFileAge(): integer; overload; - function GetFileAge(out FileDateTime: TDateTime): boolean; overload; - function Exists(): boolean; - function IsFile(): boolean; - function IsDirectory(): boolean; - function GetAttr(): cardinal; - function SetAttr(Attr: Integer): boolean; - function IsReadOnly(): boolean; - function SetReadOnly(ReadOnly: boolean): boolean; - function ForceDirectories(): boolean; //- - function FileSearch(const DirList: IPath): IPath; //- - function Rename(const NewName: IPath): boolean; - function DeleteFile(): boolean; - function DeleteEmptyDir(): boolean; - function CopyFile(const Target: IPath; FailIfExists: boolean): boolean; - end; - -function Path(const Name: AnsiString; Encoding: TPathEncoding = pencSystemANSI): IPath; overload; -function Path(const Name: WideString): IPath; overload; -function Path(PathComponents: array of const): IPath; overload; - -implementation - -uses - UFilesystem, - SysUtils; - -type - TPathImpl = class(TInterfacedObject, IPath) - private - fName: AnsiString; - public - constructor Create(const Name: string); - destructor Destroy(); override; - - function ToString(Encoding: TPathEncoding = pencSystemANSI): AnsiString; - function ToWideString(): WideString; - - //function Append(const IPath): IPath; - function Adjust(): boolean; - {** - * Removes trailing path-delimiter and replaces path-delimiters with '/'. - *} - procedure Unify(); - function Expand(): IPath; - - (* - function IncludeTrailingPathDelimiter(): IPath; - function ExcludeTrailingPathDelimiter(): IPath; - function ChangeFileExt(const Extension: IPath): IPath; - function ExtractFilePath(): IPath; - function ExtractFileDir(): IPath; - function ExtractFileDrive(): IPath; - function ExtractFileName(): IPath; - function ExtractFileExt(): IPath; - function ExtractRelativePath(const BaseName: IPath): IPath; - *) - - function CreateFile(): integer; - function CreateDir(): boolean; - function Open(Mode: LongWord): integer; - function OpenStream(Mode: Word): TUniFileStream; - function GetFileAge(): integer; overload; - function GetFileAge(out FileDateTime: TDateTime): boolean; overload; - function Exists(): boolean; - function IsFile(): boolean; - function IsDirectory(): boolean; - function GetAttr(): cardinal; - function SetAttr(Attr: Integer): boolean; - function IsReadOnly(): boolean; - function SetReadOnly(ReadOnly: boolean): boolean; - function ForceDirectories(): boolean; - function FileSearch(const DirList: IPath): IPath; - function Rename(const NewName: IPath): boolean; - function DeleteFile(): boolean; - function DeleteEmptyDir(): boolean; - function CopyFile(const Target: IPath; FailIfExists: boolean): boolean; - end; - - IPathIterator = interface - function Next(): IPath; - end; - - TPathIteratorImpl = class(TInterfacedObject, IPathIterator) - private - fPath: IPath; - fPos: integer; - public - constructor Create(Path: IPath); - - function Next(): IPath; - end; - -function Path(const Name: AnsiString; Encoding: TPathEncoding): IPath; -begin - {$IFDEF MSWINDOWS} - case Encoding of - pencUTF8: - // TODO: Check if UTF8 - Result := TPathImpl.Create(Name); - pencSystemANSI: - Result := TPathImpl.Create(AnsiToUtf8(Name)); - else - raise Exception.Create('Unhandled encoding'); - end; - {$ELSE} - Result := TPathImpl.Create(Name); - {$ENDIF} -end; - -function Path(const Name: WideString): IPath; -begin - Result := TPathImpl.Create(UTF8Encode(Name)); -end; - -function Path(PathComponents: array of const): IPath; -var - CompIndex: integer; - Name: string; -begin - Name := ''; - for CompIndex := 0 to High(PathComponents) do - begin - with PathComponents[CompIndex] do - begin - case (VType) of - vtString: - Name := Name + VString^; - vtAnsiString: - Name := Name + AnsiString(VAnsiString); - vtWideString: - Name := Name + UTF8Encode(WideString(VWideString)); - vtObject: - raise Exception.Create('Unhandled Object type'); - else - raise Exception.Create('Unhandled Path type'); - end; - end; - Name := Name + PathDelim; - end; - Result := Path(Name); -end; - -constructor TPathImpl.Create(const Name: string); -begin - inherited Create(); - fName := Name; - Unify(); -end; - -destructor TPathImpl.Destroy(); -begin - inherited; -end; - -procedure TPathImpl.Unify(); -var - I: integer; -begin - for I := 1 to Length(fName) do - begin - if (fName[I] in ['\', '/']) then - fName[I] := PathDelim; - end; - fName := ExcludeTrailingPathDelimiter(fName); -end; - -function TPathImpl.ToString(Encoding: TPathEncoding): AnsiString; -begin - {$IFDEF MSWINDOWS} - case Encoding of - pencUTF8: - Result := fName; - pencSystemANSI: - Result := Utf8ToAnsi(fName); - else - raise Exception.Create('Unhandled encoding'); - end; - {$ELSE} - Result := fName; - {$ENDIF} -end; - -function TPathImpl.ToWideString(): WideString; -begin - Result := UTF8Decode(fName); -end; - -function TPathImpl.Adjust(): boolean; -begin - Result := false; //TODO -end; - - - -function TPathImpl.Expand(): IPath; -begin - Result := FileSystem.ExpandFileName(Self); -end; - -function TPathImpl.CreateFile(): integer; -begin - Result := FileSystem.FileCreate(Self); -end; - -function TPathImpl.CreateDir(): boolean; -begin - Result := FileSystem.DirectoryCreate(Self); -end; - -function TPathImpl.Open(Mode: LongWord): integer; -begin - Result := FileSystem.FileOpen(Self, Mode); -end; - -function TPathImpl.OpenStream(Mode: Word): TUniFileStream; -begin - Result := TUniFileStream.Create(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 - Result := IsFile() or IsDirectory(); -end; - -function TPathImpl.IsFile(): boolean; -begin - Result := FileSystem.FileExists(Self); -end; - -function TPathImpl.IsDirectory(): boolean; -begin - Result := FileSystem.DirectoryExists(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.ForceDirectories(): boolean; -begin - Result := FileSystem.ForceDirectories(Self); -end; - -function TPathImpl.FileSearch(const DirList: IPath): IPath; -begin - 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; - - - -{ TPathIteratorImpl } - -constructor TPathIteratorImpl.Create(Path: IPath); -begin - fPath := Path; - fPos := -1; -end; - -function TPathIteratorImpl.Next(): IPath; -begin -end; - - -{ TUniFileStream } - -{$IFDEF MSWINDOWS} - -constructor TUniFileStream.Create(const FileName: IPath; Mode: Word); -begin - inherited Create(FileName.ToWideString(), Mode); -end; - -{$ELSE} - -constructor TUniFileStream.Create(const FileName: IPath; Mode: Word); -begin - inherited Create(FileName.ToSystemString(), Mode); -end; - -{$ENDIF} - -{ TUniMemoryStream } - -procedure TUniMemoryStream.LoadFromFile(const FileName: IPath); -var - Stream: TStream; -begin - Stream := TUniFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite); - try - LoadFromStream(Stream); - finally - Stream.Free; - end; -end; - -procedure TUniMemoryStream.SaveToFile(const FileName: IPath); -var - Stream: TStream; -begin - Stream := TUniFileStream.Create(FileName, fmCreate); - try - SaveToStream(Stream); - finally - Stream.Free; - end; -end; - -end. diff --git a/unicode/src/base/UFiles.pas b/unicode/src/base/UFiles.pas index bfcd63d2..9996eeb1 100644 --- a/unicode/src/base/UFiles.pas +++ b/unicode/src/base/UFiles.pas @@ -34,10 +34,12 @@ interface uses SysUtils, + Classes, ULog, UMusic, USongs, - USong; + USong, + UPath; procedure ResetSingTemp; @@ -48,7 +50,7 @@ type * 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: string; Relative: boolean): TSaveSongResult; +function SaveSong(const Song: TSong; const Lines: TLines; const Name: IPath; Relative: boolean): TSaveSongResult; implementation @@ -81,7 +83,7 @@ end; //-------------------- // Saves a Song //-------------------- -function SaveSong(const Song: TSong; const Lines: TLines; const Name: string; Relative: boolean): TSaveSongResult; +function SaveSong(const Song: TSong; const Lines: TLines; const Name: IPath; Relative: boolean): TSaveSongResult; var C: integer; N: integer; @@ -89,9 +91,9 @@ var B: integer; RelativeSubTime: integer; NoteState: AnsiString; - SongFile: TextFile; + SongFile: TTextFileStream; - function EncodeToken(const Str: UTF8String): AnsiString; + function EncodeToken(const Str: UTF8String): RawByteString; var Success: boolean; begin @@ -100,89 +102,87 @@ var SaveSong := ssrEncodingError; end; - function EncodeFilename(const Filename: WideString): AnsiString; - begin - // TODO: Unicode - Result := Filename; - end; - begin // Relative := true; // override (idea - use shift+S to save with relative) - AssignFile(SongFile, Name); - Rewrite(SongFile); - Result := ssrOK; + SongFile := nil; - if (Song.Encoding = encUTF8) then - Write(SongFile, UTF8_BOM); + try + SongFile := TTextFileStream.Create(Name, fmCreate); - Writeln(SongFile, '#ENCODING:' + EncodingName(Song.Encoding)); - Writeln(SongFile, '#TITLE:' + EncodeToken(Song.Title)); - Writeln(SongFile, '#ARTIST:' + EncodeToken(Song.Artist)); + if (Song.Encoding = encUTF8) then + SongFile.Write(UTF8_BOM); - if Song.Creator <> '' then Writeln(SongFile, '#CREATOR:' + EncodeToken(Song.Creator)); - if Song.Edition <> 'Unknown' then Writeln(SongFile, '#EDITION:' + EncodeToken(Song.Edition)); - if Song.Genre <> 'Unknown' then Writeln(SongFile, '#GENRE:' + EncodeToken(Song.Genre)); - if Song.Language <> 'Unknown' then Writeln(SongFile, '#LANGUAGE:' + EncodeToken(Song.Language)); + SongFile.WriteLine('#ENCODING:' + EncodingName(Song.Encoding)); + SongFile.WriteLine('#TITLE:' + EncodeToken(Song.Title)); + SongFile.WriteLine('#ARTIST:' + EncodeToken(Song.Artist)); - Writeln(SongFile, '#MP3:' + EncodeFilename(Song.Mp3)); - if Song.Cover <> '' then Writeln(SongFile, '#COVER:' + EncodeFilename(Song.Cover)); - if Song.Background <> '' then Writeln(SongFile, '#BACKGROUND:' + EncodeFilename(Song.Background)); - if Song.Video <> '' then Writeln(SongFile, '#VIDEO:' + EncodeFilename(Song.Video)); + 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.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'); + 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)); - Writeln(SongFile, '#BPM:' + FloatToStr(Song.BPM[0].BPM / 4)); - Writeln(SongFile, '#GAP:' + FloatToStr(Song.GAP)); + 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'); - RelativeSubTime := 0; - for B := 1 to High(Song.BPM) do - Writeln(SongFile, 'B ' + FloatToStr(Song.BPM[B].StartBeat) + ' ' - + FloatToStr(Song.BPM[B].BPM/4)); + SongFile.WriteLine('#BPM:' + FloatToStr(Song.BPM[0].BPM / 4)); + SongFile.WriteLine('#GAP:' + FloatToStr(Song.GAP)); - for C := 0 to Lines.High do - begin - for N := 0 to Lines.Line[C].HighNote do + 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 - with Lines.Line[C].Note[N] do + for N := 0 to Lines.Line[C].HighNote 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); - - Writeln(SongFile, 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 + 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 - S := '- ' + IntToStr(Lines.Line[C+1].Start - RelativeSubTime) + - ' ' + IntToStr(Lines.Line[C+1].Start - RelativeSubTime); - RelativeSubTime := Lines.Line[C+1].Start; + 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; - Writeln(SongFile, S); - end; - end; // C + end; // C - Writeln(SongFile, 'E'); + SongFile.WriteLine('E'); + except + Result := ssrFileError; + end; - CloseFile(SongFile); + SongFile.Free; end; end. diff --git a/unicode/src/base/UFilesystem.pas b/unicode/src/base/UFilesystem.pas index 480d3376..1cfb6837 100644 --- a/unicode/src/base/UFilesystem.pas +++ b/unicode/src/base/UFilesystem.pas @@ -223,7 +223,7 @@ end; function TFileSystemImpl.CreateFileStream(const FileName: IPath; Mode: Word): THandleStream; begin - Result := TUnicodeFileStream.Create(FileName, Mode); + Result := TBinaryFileStream.Create(FileName, Mode); end; function TFileSystemImpl.FileFind(const FilePattern: IPath; Attr: Integer): IFileIterator; diff --git a/unicode/src/base/UFont.pas b/unicode/src/base/UFont.pas index ce52ed96..d91dca59 100644 --- a/unicode/src/base/UFont.pas +++ b/unicode/src/base/UFont.pas @@ -47,13 +47,14 @@ uses glext, glu, sdl, + Math, + Classes, + SysUtils, UUnicodeUtils, {$IFDEF BITMAP_FONT} UTexture, {$ENDIF} - Math, - Classes, - SysUtils; + UPath; type @@ -506,7 +507,7 @@ type procedure ResetIntern(); protected - fFilename: AnsiString; //**< 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 @@ -537,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: AnsiString; + constructor Create(const Filename: IPath; Size: integer; Outset: single = 0.0; LoadFlags: FT_Int32 = FT_LOAD_DEFAULT); @@ -567,7 +568,7 @@ type * The extrusion in pixels is Size*OutsetAmount * (0.0 -> no extrusion, 0.1 -> 10%). *} - constructor Create(const Filename: AnsiString; + constructor Create(const Filename: IPath; Size: integer; OutsetAmount: single = 0.0; UseMipmaps: boolean = true); @@ -585,7 +586,7 @@ type *} TFTOutlineFont = class(TFont) private - fFilename: AnsiString; + fFilename: IPath; fSize: integer; fOutset: single; fInnerFont, fOutlineFont: TFTFont; @@ -612,7 +613,7 @@ type procedure SetReflectionPass(Enable: boolean); override; public - constructor Create(const Filename: AnsiString; + constructor Create(const Filename: IPath; Size: integer; Outset: single; LoadFlags: FT_Int32 = FT_LOAD_DEFAULT); destructor Destroy; override; @@ -646,7 +647,7 @@ type function CreateMipmap(Level: integer; Scale: single): TFont; override; public - constructor Create(const Filename: AnsiString; + constructor Create(const Filename: IPath; Size: integer; OutsetAmount: single; UseMipmaps: boolean = true); @@ -688,7 +689,7 @@ type * @param InfoFile the name of the info (.dat) file * @raises Exception if the file is corrupted *} - procedure LoadFontInfo(const InfoFile: AnsiString); + procedure LoadFontInfo(const InfoFile: IPath); protected procedure Render(const Text: UCS4String); override; @@ -708,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: AnsiString; Outline: integer; + constructor Create(const Filename: IPath; Outline: integer; Baseline, Ascender, Descender: integer); destructor Destroy(); override; @@ -1406,7 +1407,7 @@ end; *} constructor TFTFont.Create( - const Filename: AnsiString; + const Filename: IPath; Size: integer; Outset: single; LoadFlags: FT_Int32); var @@ -1422,8 +1423,8 @@ begin 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 @@ -1645,7 +1646,7 @@ end; * TFTScalableFont *} -constructor TFTScalableFont.Create(const Filename: AnsiString; +constructor TFTScalableFont.Create(const Filename: IPath; Size: integer; OutsetAmount: single; UseMipmaps: boolean); var @@ -1701,7 +1702,7 @@ end; *} constructor TFTOutlineFont.Create( - const Filename: AnsiString; + const Filename: IPath; Size: integer; Outset: single; LoadFlags: FT_Int32); begin @@ -1893,7 +1894,7 @@ end; *} constructor TFTScalableOutlineFont.Create( - const Filename: AnsiString; + const Filename: IPath; Size: integer; OutsetAmount: single; UseMipmaps: boolean); var @@ -2555,7 +2556,7 @@ end; * TBitmapFont *} -constructor TBitmapFont.Create(const Filename: AnsiString; Outline: integer; +constructor TBitmapFont.Create(const Filename: IPath; Outline: integer; Baseline, Ascender, Descender: integer); begin inherited Create(); @@ -2567,7 +2568,7 @@ begin fAscender := Ascender; fDescender := Descender; - LoadFontInfo(ChangeFileExt(Filename, '.dat')); + LoadFontInfo(Filename.SetExtension('.dat')); ResetIntern(); end; @@ -2597,18 +2598,18 @@ begin fWidths[Count] := Round(fWidths[Count] * WidthMult) + WidthAdd; end; -procedure TBitmapFont.LoadFontInfo(const InfoFile: AnsiString); +procedure TBitmapFont.LoadFontInfo(const InfoFile: IPath); var - Stream: TFileStream; + Stream: THandleStream; 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; diff --git a/unicode/src/base/UGraphic.pas b/unicode/src/base/UGraphic.pas index 7c5b18fd..9c12d998 100644 --- a/unicode/src/base/UGraphic.pas +++ b/unicode/src/base/UGraphic.pas @@ -340,7 +340,7 @@ begin Tex_Cursor_Unpressed := Texture.LoadTexture(Skin.GetTextureFileName('Cursor'), TEXTURE_TYPE_TRANSPARENT, 0); - if (Skin.GetTextureFileName('Cursor_Pressed') <> '') then + 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; @@ -389,14 +389,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; @@ -411,23 +411,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'); @@ -464,7 +464,7 @@ 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); diff --git a/unicode/src/base/UImage.pas b/unicode/src/base/UImage.pas index 60b0a3a2..25b0d1e2 100644 --- a/unicode/src/base/UImage.pas +++ b/unicode/src/base/UImage.pas @@ -34,7 +34,8 @@ interface {$I switches.inc} uses - SDL; + SDL, + UPath; {$DEFINE HavePNG} {$DEFINE HaveBMP} @@ -131,20 +132,20 @@ 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 @@ -282,17 +283,17 @@ end; procedure user_read_data(png_ptr: png_structp; data: png_bytep; length: png_size_t); cdecl; var - inFile: TFileStream; + inFile: THandleStream; begin - inFile := TFileStream(png_get_io_ptr(png_ptr)); + inFile := THandleStream(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: THandleStream; begin - outFile := TFileStream(png_get_io_ptr(png_ptr)); + outFile := THandleStream(png_get_io_ptr(png_ptr)); outFile.Write(data^, length); end; @@ -323,11 +324,11 @@ 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: THandleStream; row: integer; rowData: array of png_bytep; // rowStride: integer; @@ -339,9 +340,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; @@ -500,9 +501,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: THandleStream; FileInfo: BITMAPINFOHEADER; FileHeader: BITMAPFILEHEADER; Converted: boolean; @@ -513,9 +514,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; @@ -579,7 +580,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 @@ -597,7 +598,7 @@ 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; @@ -676,9 +677,9 @@ begin try // compress image (don't forget this line, otherwise it won't be compressed) Jpeg.Compress(); - Jpeg.SaveToFile(FileName); + Jpeg.SaveToFile(FileName.ToNative); except - Log.LogError('Could not save file: "' + FileName + '"', 'WriteJPGImage'); + Log.LogError('Could not save file: "' + FileName.ToNative + '"', 'WriteJPGImage'); Exit; end; Jpeg.Free; @@ -763,27 +764,25 @@ end; (* * 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; begin - Result := nil; + Result := nil; - // FileExistsInsensitive() requires a var-arg - FilenameFound := Filename; - - // 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)); + Result := IMG_Load(PChar(FilenameCaseAdj.ToNative)); except - Log.LogError('Could not load from file "'+FilenameFound+'"', 'LoadImage'); + Log.LogError('Could not load from file "' + FilenameCaseAdj.ToNative + '"', 'LoadImage'); Exit; end; end; diff --git a/unicode/src/base/UIni.pas b/unicode/src/base/UIni.pas index bf068f11..85f925ad 100644 --- a/unicode/src/base/UIni.pas +++ b/unicode/src/base/UIni.pas @@ -36,9 +36,11 @@ interface uses Classes, IniFiles, + SysUtils, ULog, UTextEncoding, - SysUtils; + UFilesystem, + UPath; type // TInputDeviceConfig stores the configuration for an input device. @@ -71,11 +73,10 @@ 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 LoadInputDeviceCfg(IniFile: TMemIniFile); @@ -92,7 +93,7 @@ type NameTemplate: array[0..11] of UTF8String; //Filename of the opened iniFile - Filename: string; + Filename: IPath; // Game Players: integer; @@ -165,19 +166,19 @@ type var Ini: TIni; - IResolution: array of string; - ILanguage: array of string; - ITheme: array of string; - ISkin: array of string; + 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..7] of UTF8String = ('Edition', 'Genre', 'Language', 'Folder', 'Title', 'Artist', 'Title2', 'Artist2'); sEdition = 0; sGenre = 1; sLanguage = 2; @@ -187,71 +188,71 @@ const sTitle2 = 6; sArtist2 = 7; - IDebug: array[0..1] of string = ('Off', 'On'); + IDebug: array[0..1] of UTF8String = ('Off', 'On'); - 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'); + 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 string = ('Off', 'On'); + IBackgroundMusic: array[0..1] of UTF8String = ('Off', 'On'); - ITextureSize: array[0..3] of string = ('64', '128', '256', '512'); - ITextureSizeVals: array[0..3] of integer = ( 64, 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..1] of string = ('Off', 'On'); + IOscilloscope: 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]'); + 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 string = ('Off', 'On'); - IBeatClick: array[0..1] of string = ('Off', 'On'); - ISavePlayback: array[0..1] of string = ('Off', 'On'); + 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 string = ('5%', '10%', '15%', '20%'); + 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 string = ('Off', 'On'); + IVoicePassthrough: array[0..1] of UTF8String = ('Off', 'On'); - 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 ); + 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 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 ); + 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 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 ); + 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 string = ('Off', '1 Sec', '2 Secs', '3 Secs', '4 Secs', '5 Secs'); - IPreviewFadingVals: array[0..5] of integer = ( 0, 1, 2, 3, 4, 5 ); + 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 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'); + 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'); - IColor: array[0..8] of string = ('Blue', 'Green', 'Pink', 'Red', 'Violet', 'Orange', 'Yellow', 'Brown', 'Black'); + IColor: 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..1] of string = ('Off', 'On'); - IPartyPopup: array[0..1] of string = ('Off', 'On'); + 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'); + ILineBonus: array[0..1] of UTF8String = ('Off', 'On'); + IPartyPopup: array[0..1] of UTF8String = ('Off', 'On'); - IJoypad: array[0..1] of string = ('Off', 'On'); - IMouse: array[0..2] of string = ('Off', 'Hardware Cursor', 'Software Cursor'); + 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 string = ('Off', '1', '2', '3', '4', '5', '6'); - IMicBoost: array[0..3] of string = ('Off', '+6dB', '+12dB', '+18dB'); + IChannelPlayer: array[0..6] of UTF8String = ('Off', '1', '2', '3', '4', '5', '6'); + IMicBoost: array[0..3] of UTF8String = ('Off', '+6dB', '+12dB', '+18dB'); implementation @@ -267,14 +268,6 @@ uses UPathUtils, UUnicodeUtils; -(** - * Returns the filename without its fileextension - *) -function TIni.RemoveFileExt(FullName: string): string; -begin - Result := ChangeFileExt(FullName, ''); -end; - (** * Extracts an index of a key that is surrounded by a Prefix/Suffix pair. * Example: ExtractKeyIndex('MyKey[1]', '[', ']') will return 1. @@ -325,7 +318,7 @@ 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; +function TIni.GetArrayIndex(const SearchArray: array of UTF8String; Value: string; CaseInsensitiv: boolean = false): integer; var i: integer; @@ -349,7 +342,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; @@ -464,7 +457,7 @@ begin begin if (Pos('SONGDIR', UpperCase(PathStrings[I])) = 1) then begin - AddSongPath(IniFile.ReadString('Directories', PathStrings[I], '')); + AddSongPath(Path(IniFile.ReadString('Directories', PathStrings[I], ''))); end; end; @@ -477,18 +470,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 @@ -497,12 +495,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 @@ -523,7 +520,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 @@ -631,19 +628,15 @@ var 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 @@ -784,13 +777,13 @@ procedure TIni.Save; var 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]); @@ -930,9 +923,9 @@ 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 := 0 to High(Name) do @@ -950,9 +943,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/unicode/src/base/ULanguage.pas b/unicode/src/base/ULanguage.pas index 9571358e..88b6647f 100644 --- a/unicode/src/base/ULanguage.pas +++ b/unicode/src/base/ULanguage.pas @@ -79,6 +79,8 @@ uses Classes, SysUtils, ULog, + UPath, + UFilesystem, UPathUtils; {** @@ -129,23 +131,26 @@ end; *} 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 + Iter := FileSystem.FileFind(LanguagesPath.Append('*.ini'), 0); + while(Iter.HasNext) do begin - repeat - SetLength(List, Length(List)+1); - SetLength(ILanguage, Length(ILanguage)+1); - SR.Name := ChangeFileExt(SR.Name, ''); - - List[High(List)].Name := SR.Name; - ILanguage[High(ILanguage)] := SR.Name; - until (FindNext(SR) <> 0); - SysUtils.FindClose(SR); - end; // if FindFirst + IniInfo := Iter.Next; + + LangName := IniInfo.Name.SetExtension('').ToUTF8; + + SetLength(List, Length(List)+1); + List[High(List)].Name := LangName; + + SetLength(ILanguage, Length(ILanguage)+1); + ILanguage[High(ILanguage)] := LangName; + end; end; {** @@ -158,7 +163,7 @@ var S: TStringList; begin SetLength(Entry, 0); - IniFile := TIniFile.Create(LanguagesPath + Language + '.ini'); + IniFile := TIniFile.Create(LanguagesPath.Append(Language + '.ini').ToNative); S := TStringList.Create; IniFile.ReadSectionValues('Text', S); diff --git a/unicode/src/base/ULog.pas b/unicode/src/base/ULog.pas index 6f7aa644..08a64e2b 100644 --- a/unicode/src/base/ULog.pas +++ b/unicode/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 @@ -198,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 @@ -270,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 @@ -399,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); diff --git a/unicode/src/base/UMain.pas b/unicode/src/base/UMain.pas index 5b07c9ae..6ab4e187 100644 --- a/unicode/src/base/UMain.pas +++ b/unicode/src/base/UMain.pas @@ -196,7 +196,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); @@ -252,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); diff --git a/unicode/src/base/UMusic.pas b/unicode/src/base/UMusic.pas index b959ea05..51759b6f 100644 --- a/unicode/src/base/UMusic.pas +++ b/unicode/src/base/UMusic.pas @@ -34,10 +34,11 @@ interface {$I switches.inc} uses - UTime, SysUtils, Classes, - UBeatTimer; + UTime, + UBeatTimer, + UPath; type TNoteType = (ntFreestyle, ntNormal, ntGolden); @@ -315,7 +316,7 @@ type // soundcard output-devices information TAudioOutputDevice = class public - Name: string; // soundcard name + Name: UTF8String; // soundcard name end; TAudioOutputDeviceList = array of TAudioOutputDevice; @@ -324,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; @@ -376,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); @@ -391,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; @@ -406,7 +407,7 @@ type IAudioDecoder = Interface( IGenericDecoder ) ['{AB47B1B6-2AA9-4410-BF8C-EC79561B5478}'] - function Open(const Filename: string): TAudioDecodeStream; + function Open(const Filename: IPath): TAudioDecodeStream; end; IAudioInput = Interface @@ -497,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; @@ -654,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); @@ -671,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; @@ -686,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; @@ -719,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; @@ -734,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; @@ -748,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; @@ -772,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; @@ -855,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; diff --git a/unicode/src/base/UPath.pas b/unicode/src/base/UPath.pas index 5203e7eb..fc8c028e 100644 --- a/unicode/src/base/UPath.pas +++ b/unicode/src/base/UPath.pas @@ -45,14 +45,17 @@ type IPath = interface; {** - * TUnicodeFileStream + * TBinaryFileStream (inherited from THandleStream) *} {$IFDEF MSWINDOWS} - TUnicodeFileStream = class(TTntFileStream) + TBinaryFileStream = class(TTntFileStream) {$ELSE} - TUnicodeFileStream = class(TFileStream) + TBinaryFileStream = class(TFileStream) {$ENDIF} public + {** + * @seealso TFileStream.Create for valid Mode parameters + *} constructor Create(const FileName: IPath; Mode: Word); end; @@ -65,13 +68,28 @@ type procedure SaveToFile(const FileName: IPath); end; + TTextFileStream = class(TBinaryFileStream) + function ReadLine(var Line: UTF8String): boolean; overload; + function ReadLine(var Line: AnsiString): boolean; overload; + procedure Write(Str: RawByteString); + procedure WriteLine(Line: RawByteString = ''); + 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; - + {** * IPath * The Path's pathname is immutable and cannot be changed after creation. *} 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) @@ -100,11 +118,6 @@ type *} function Open(Mode: LongWord): THandle; - {** - * Note: Stream must be freed with TUniFileStream.Free after usage - *} - function OpenStream(Mode: Word): TUnicodeFileStream; - {** @seealso SysUtils.ExtractFileDrive() *} function GetDrive(): IPath; @@ -125,7 +138,9 @@ type * The file itself is not changed, use Rename() for this task. * @seealso SysUtils.ChangeFileExt() *} - function SetExtension(const Extension: IPath): IPath; + 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. @@ -143,16 +158,19 @@ type * 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): IPath; + 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. - * Example: C:\test\dir -> ['C:\', 'test\', 'dir'] + * 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 PathNone if none exists. + * Returns the parent directory or PATH_NONE if none exists. *} function GetParent(): IPath; @@ -171,10 +189,10 @@ type function AdjustCase(AdjustAllLevels: boolean): IPath; {** @seealso SysUtils.IncludeTrailingPathDelimiter() *} - function IncludeTrailingPathDelimiter(): IPath; + function AppendPathDelim(): IPath; {** @seealso SysUtils.ExcludeTrailingPathDelimiter() *} - function ExcludeTrailingPathDelimiter(): IPath; + function RemovePathDelim(): IPath; function Exists(): boolean; function IsFile(): boolean; @@ -187,13 +205,29 @@ type 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; + 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 @@ -222,17 +256,17 @@ type * - 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): IPath; overload; +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): IPath; overload; +function Path(const PathName: WideString; DelimOption: TPathDelimOption = pdKeep): IPath; overload; {** - * Returns a reference to a singleton with path simply set to ''. + * Returns a singleton for Path(''). *} -function PathNone(): IPath; +function PATH_NONE(): IPath; implementation @@ -247,7 +281,7 @@ type {** * Unifies the filename. Path-delimiters are replaced by '/'. *} - procedure Unify(); + procedure Unify(DelimOption: TPathDelimOption); {** * Returns a copy of fName with path delimiters changed to '/'. @@ -257,7 +291,7 @@ type procedure AssertRefCount; inline; public - constructor Create(const Name: UTF8String); + constructor Create(const Name: UTF8String; DelimOption: TPathDelimOption); destructor Destroy(); override; function ToUTF8(UseNativeDelim: boolean): UTF8String; @@ -265,27 +299,36 @@ type function ToNative(): RawByteString; function Open(Mode: LongWord): THandle; - function OpenStream(Mode: Word): TUnicodeFileStream; function GetDrive(): IPath; function GetPath(): IPath; function GetDir(): IPath; function GetName(): IPath; function GetExtension(): IPath; - function SetExtension(const Extension: IPath): 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): IPath; - function Equals(const Other: IPath; IgnoreCase: boolean): boolean; + 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 IncludeTrailingPathDelimiter(): IPath; - function ExcludeTrailingPathDelimiter(): IPath; + function AppendPathDelim(): IPath; + function RemovePathDelim(): IPath; function GetFileAge(): integer; overload; function GetFileAge(out FileDateTime: TDateTime): boolean; overload; @@ -297,7 +340,10 @@ type 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; @@ -308,34 +354,36 @@ type function CopyFile(const Target: IPath; FailIfExists: boolean): boolean; end; -function Path(const PathName: RawByteString): IPath; +function Path(const PathName: RawByteString; DelimOption: TPathDelimOption): IPath; begin if (IsUTF8String(PathName)) then - Result := TPathImpl.Create(PathName) + Result := TPathImpl.Create(PathName, DelimOption) else if (IsNativeUTF8()) then - Result := PathNone + Result := PATH_NONE else - Result := TPathImpl.Create(AnsiToUtf8(PathName)); + Result := TPathImpl.Create(AnsiToUtf8(PathName), DelimOption); end; -function Path(const PathName: WideString): IPath; +function Path(const PathName: WideString; DelimOption: TPathDelimOption): IPath; begin - Result := TPathImpl.Create(UTF8Encode(PathName)); + Result := TPathImpl.Create(UTF8Encode(PathName), DelimOption); end; procedure TPathImpl.AssertRefCount; begin + {$IFDEF FPC} if (FRefCount <= 0) then raise Exception.Create('RefCount error: ' + inttostr(FRefCount)); + {$ENDIF} end; -constructor TPathImpl.Create(const Name: UTF8String); +constructor TPathImpl.Create(const Name: UTF8String; DelimOption: TPathDelimOption); begin inherited Create(); fName := Name; - Unify(); + Unify(DelimOption); end; destructor TPathImpl.Destroy(); @@ -343,7 +391,7 @@ begin inherited; end; -procedure TPathImpl.Unify(); +procedure TPathImpl.Unify(DelimOption: TPathDelimOption); var I: integer; begin @@ -353,6 +401,12 @@ 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; @@ -430,6 +484,16 @@ begin 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; @@ -448,9 +512,9 @@ var begin AssertRefCount; - Result := PathNone; + Result := PATH_NONE; - CurPath := Self.ExcludeTrailingPathDelimiter(); + CurPath := Self.RemovePathDelim(); // check if current path has a parent (no further '/') if (Pos(PathDelim, CurPath.ToUTF8()) = 0) then Exit; @@ -491,7 +555,7 @@ begin // TODO: remove this workaround for FPC bug TmpPath := CurPath; CurPath := TmpPath.GetParent(); - until (CurPath = PathNone); + until (CurPath = PATH_NONE); // reverse list SetLength(Result, Length(Components)); @@ -499,28 +563,56 @@ begin Result[I] := Components[High(Components)-I]; end; -function TPathImpl.Append(const Child: IPath): IPath; +function TPathImpl.Append(const Child: IPath; DelimOption: TPathDelimOption): IPath; +var + TmpResult: IPath; begin AssertRefCount; if (fName = '') then - Result := Child + TmpResult := Child else - Result := Path(Self.IncludeTrailingPathDelimiter().ToUTF8() + Child.ToUTF8()); + 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 + Result := Append(Path(Child), DelimOption); +end; + +function TPathImpl.Append(const Child: WideString; DelimOption: TPathDelimOption): IPath; +begin + Result := Append(Path(Child), DelimOption); end; function TPathImpl.Equals(const Other: IPath; IgnoreCase: boolean): boolean; var SelfPath, OtherPath: UTF8String; begin - SelfPath := Self.GetAbsolutePath().ExcludeTrailingPathDelimiter().ToUTF8(); - OtherPath := Other.GetAbsolutePath().ExcludeTrailingPathDelimiter().ToUTF8(); + 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; @@ -533,11 +625,11 @@ begin // TODO: remove workaround for fpc refcount bug TmpPath := Self.GetParent(); TmpPath2 := TmpPath.GetAbsolutePath(); - SelfPath := TmpPath2.IncludeTrailingPathDelimiter().ToUTF8(); + SelfPath := TmpPath2.AppendPathDelim().ToUTF8(); // TODO: remove workaround for fpc refcount bug TmpPath := Parent.GetAbsolutePath(); - ParentPath := TmpPath.IncludeTrailingPathDelimiter().ToUTF8(); + ParentPath := TmpPath.AppendPathDelim().ToUTF8(); // simply check if this paths parent path (SelfPath) equals ParentPath Result := (SelfPath = ParentPath); @@ -546,11 +638,11 @@ begin begin // TODO: remove workaround for fpc refcount bug TmpPath := Self.GetAbsolutePath(); - SelfPath := TmpPath.IncludeTrailingPathDelimiter().ToUTF8(); + SelfPath := TmpPath.AppendPathDelim().ToUTF8(); // TODO: remove workaround for fpc refcount bug TmpPath := Parent.GetAbsolutePath(); - ParentPath := TmpPath.IncludeTrailingPathDelimiter().ToUTF8(); + ParentPath := TmpPath.AppendPathDelim().ToUTF8(); if (Length(SelfPath) <= Length(ParentPath)) then Exit; @@ -583,12 +675,12 @@ begin // extract name component of current path // TODO: remove workaround for fpc refcount bug - TmpPath := CurPath.ExcludeTrailingPathDelimiter(); + TmpPath := CurPath.RemovePathDelim(); LocalName := TmpPath.GetName(); // try to adjust parent OldParent := CurPath.GetParent(); - if (OldParent <> PathNone) then + if (OldParent <> PATH_NONE) then begin if (not AdjustAllLevels) then begin @@ -663,13 +755,13 @@ begin end; end; -function TPathImpl.IncludeTrailingPathDelimiter(): IPath; +function TPathImpl.AppendPathDelim(): IPath; begin AssertRefCount; Result := FileSystem.IncludeTrailingPathDelimiter(Self); end; -function TPathImpl.ExcludeTrailingPathDelimiter(): IPath; +function TPathImpl.RemovePathDelim(): IPath; begin AssertRefCount; Result := FileSystem.ExcludeTrailingPathDelimiter(Self); @@ -693,11 +785,6 @@ begin Result := FileSystem.FileOpen(Self, Mode); end; -function TPathImpl.OpenStream(Mode: Word): TUnicodeFileStream; -begin - Result := TUnicodeFileStream.Create(Self, Mode); -end; - function TPathImpl.GetFileAge(): integer; begin Result := FileSystem.FileAge(Self); @@ -759,6 +846,16 @@ 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; @@ -786,23 +883,38 @@ begin end; -{ TUnicodeFileStream } +{ 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; + +{ TTextFileStream } -constructor TUnicodeFileStream.Create(const FileName: IPath; Mode: Word); +function TTextFileStream.ReadLine(var Line: UTF8String): boolean; begin - inherited Create(FileName.ToWide(), Mode); + // TODO end; -{$ELSE} +function TTextFileStream.ReadLine(var Line: AnsiString): boolean; +begin + // TODO +end; -constructor TUnicodeFileStream.Create(const FileName: IPath; Mode: Word); +procedure TTextFileStream.WriteLine(Line: RawByteString); begin - inherited Create(FileName.ToNative(), Mode); + Self.WriteBuffer(Line[1], Length(Line)); end; -{$ENDIF} +procedure TTextFileStream.Write(Str: RawByteString); +begin + Self.WriteBuffer(Str[1], Length(Str)); +end; { TUnicodeMemoryStream } @@ -810,7 +922,7 @@ procedure TUnicodeMemoryStream.LoadFromFile(const FileName: IPath); var Stream: TStream; begin - Stream := TUnicodeFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite); + Stream := TBinaryFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite); try LoadFromStream(Stream); finally @@ -822,7 +934,7 @@ procedure TUnicodeMemoryStream.SaveToFile(const FileName: IPath); var Stream: TStream; begin - Stream := TUnicodeFileStream.Create(FileName, fmCreate); + Stream := TBinaryFileStream.Create(FileName, fmCreate); try SaveToStream(Stream); finally @@ -831,17 +943,17 @@ begin end; var - PathNone_Singelton: IPath; + PATH_NONE_Singelton: IPath; -function PathNone(): IPath; +function PATH_NONE(): IPath; begin - Result := PathNone_Singelton; + Result := PATH_NONE_Singelton; end; initialization - PathNone_Singelton := Path(''); + PATH_NONE_Singelton := Path(''); finalization - PathNone_Singelton := nil; + PATH_NONE_Singelton := nil; end. diff --git a/unicode/src/base/UPathUtils.pas b/unicode/src/base/UPathUtils.pas index 4c4d3bc0..c2bcdd4b 100644 --- a/unicode/src/base/UPathUtils.pas +++ b/unicode/src/base/UPathUtils.pas @@ -35,28 +35,29 @@ interface uses SysUtils, - Classes; + Classes, + UPath; 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; - -function FindPath(out PathResult: string; const RequestedPath: string; NeedsWritePermission: boolean): boolean; + 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: string); +procedure AddSongPath(const Path: IPath); implementation @@ -66,33 +67,38 @@ uses UCommandLine, ULog; -procedure AddSpecialPath(var PathList: TStringList; const Path: string); +procedure AddSpecialPath(var PathList: IInterfaceList; const Path: IPath); var Index: integer; - PathAbs, OldPathAbs: string; + PathAbs, PathTmp: IPath; + OldPath, OldPathAbs, OldPathTmp: IPath; begin if (PathList = nil) then - PathList := TStringList.Create; + PathList := TInterfaceList.Create; - if (Path = '') or not ForceDirectories(Path) then + if Path.Equals(PATH_NONE) or not Path.CreateDirectory(true) then Exit; - PathAbs := IncludeTrailingPathDelimiter(ExpandFileName(Path)); + 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 - OldPathAbs := IncludeTrailingPathDelimiter(ExpandFileName(PathList[Index])); + 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 (AnsiStartsText(OldPathAbs, PathAbs)) then + 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 (AnsiStartsText(PathAbs, OldPathAbs)) then + if (PathAbs.IsChildOf(OldPathAbs, false)) then begin // replace the old with the new one. PathList[Index] := PathAbs; @@ -103,12 +109,12 @@ begin PathList.Add(PathAbs); end; -procedure AddSongPath(const Path: string); +procedure AddSongPath(const Path: IPath); begin AddSpecialPath(SongPaths, Path); end; -procedure AddCoverPath(const Path: string); +procedure AddCoverPath(const Path: IPath); begin AddSpecialPath(CoverPaths, Path); end; @@ -117,30 +123,27 @@ end; * Initialize a path variable * After setting paths, make sure that paths exist *) -function FindPath(out PathResult: string; - const RequestedPath: string; - NeedsWritePermission: boolean) - : boolean; +function FindPath( + out PathResult: IPath; + const RequestedPath: IPath; + NeedsWritePermission: boolean): boolean; begin Result := false; - if (RequestedPath = '') then + if (RequestedPath.Equals(PATH_NONE)) then Exit; // Make sure the directory exists - if (not ForceDirectories(RequestedPath)) then + if (not RequestedPath.CreateDirectory(true)) then begin - PathResult := ''; + PathResult := PATH_NONE; Exit; end; - PathResult := IncludeTrailingPathDelimiter(RequestedPath); + PathResult := RequestedPath.AppendPathDelim(); - if (NeedsWritePermission) and - (FileIsReadOnly(RequestedPath)) then - begin + if (NeedsWritePermission) and RequestedPath.IsReadOnly() then Exit; - end; Result := true; end; @@ -149,40 +152,45 @@ 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 +'" not available', 'InitializePaths'); + Log.LogWarn('Log directory "'+ Platform.GetLogPath.ToNative +'" 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); + 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, Platform.GetGameUserPath + 'playlists', true); + FindPath(PlaylistPath, UserPath.Append('playlists'), true); // Screenshot directory (must be writable) - if (not FindPath(ScreenshotsPath, Platform.GetGameUserPath + 'screenshots', true)) then + if (not FindPath(ScreenshotsPath, UserPath.Append('screenshots'), true)) then begin - Log.LogWarn('Screenshot directory "'+ Platform.GetGameUserPath +'" not available', 'InitializePaths'); + Log.LogWarn('Screenshot directory "'+ UserPath.ToNative +'" not available', 'InitializePaths'); end; // Add song paths AddSongPath(Params.SongPath); - AddSongPath(Platform.GetGameSharedPath + 'songs'); - AddSongPath(Platform.GetGameUserPath + 'songs'); + AddSongPath(SharedPath.Append('songs')); + AddSongPath(UserPath.Append('songs')); // Add category cover paths - AddCoverPath(Platform.GetGameSharedPath + 'covers'); - AddCoverPath(Platform.GetGameUserPath + 'covers'); + AddCoverPath(SharedPath.Append('covers')); + AddCoverPath(UserPath.Append('covers')); end; end. diff --git a/unicode/src/base/UPlatform.pas b/unicode/src/base/UPlatform.pas index 6f13481c..11c67fa7 100644 --- a/unicode/src/base/UPlatform.pas +++ b/unicode/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; 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,7 +68,9 @@ uses {$ELSEIF Defined(UNIX)} UPlatformLinux, {$IFEND} - ULog; + ULog, + UUnicodeUtils, + UFilesystem; // I modified it to use the Platform_singleton in this location (in the implementation) @@ -109,9 +103,13 @@ 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; (** @@ -122,65 +120,6 @@ 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/unicode/src/base/UPlatformLinux.pas b/unicode/src/base/UPlatformLinux.pas index 7efeb963..f318d49d 100644 --- a/unicode/src/base/UPlatformLinux.pas +++ b/unicode/src/base/UPlatformLinux.pas @@ -44,15 +44,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 +58,7 @@ implementation uses UCommandLine, BaseUnix, - {$IF FPC_VERSION_INT >= 2002002} pwd, - {$IFEND} SysUtils, ULog; @@ -88,113 +84,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; - 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/unicode/src/base/UPlatformMacOSX.pas b/unicode/src/base/UPlatformMacOSX.pas index 08ad247d..aa1eb7cf 100644 --- a/unicode/src/base/UPlatformMacOSX.pas +++ b/unicode/src/base/UPlatformMacOSX.pas @@ -36,7 +36,8 @@ interface uses Classes, ULog, - UPlatform; + UPlatform, + UPath; type {** @@ -93,13 +94,13 @@ 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. *} - function GetApplicationSupportPath: WideString; + function GetApplicationSupportPath: IPath; {** * see the description of @link(Init). @@ -115,31 +116,25 @@ type *} 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/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. * 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. * This is where a user can add songs, themes, .... *} - function GetGameUserPath : WideString; override; + function GetGameUserPath : IPath; override; end; implementation @@ -248,87 +243,38 @@ begin ChDir(OldBaseDir); end; -function TPlatformMacOSX.GetBundlePath: WideString; +function TPlatformMacOSX.GetBundlePath: IPath; var - i, pos : integer; + TmpPath: 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; + TmpPath := GetExecutionDir().GetParent; + Result := TmpPath.GetParent; end; -function TPlatformMacOSX.GetApplicationSupportPath: WideString; +function TPlatformMacOSX.GetApplicationSupportPath: IPath; const PathName : string = '/Library/Application Support/UltraStarDeluxe'; + HomeDir: IPath; begin - Result := GetEnvironmentVariable('HOME') + PathName + '/'; + HomeDir := GetEnvironmentVariable('HOME'); + Result := HomeDir.Append(PathName, true); end; -function TPlatformMacOSX.GetLogPath: WideString; +function TPlatformMacOSX.GetLogPath: IPath; begin - Result := GetApplicationSupportPath + 'Logs'; + Result := GetApplicationSupportPath.Append('Logs'); end; -function TPlatformMacOSX.GetGameSharedPath: WideString; +function TPlatformMacOSX.GetGameSharedPath: IPath; begin Result := GetApplicationSupportPath; end; -function TPlatformMacOSX.GetGameUserPath: WideString; +function TPlatformMacOSX.GetGameUserPath: IPath; begin Result := GetApplicationSupportPath; end; -{ TODO: REMOVE } -function TPlatformMacOSX.DirectoryFindFiles(Dir, Filter: WideString; ReturnAllSubDirs: boolean): TDirectoryEntryArray; -var - i : integer; - TheDir : pdir; - ADirent : pDirent; - 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; -end; - end. diff --git a/unicode/src/base/UPlatformWindows.pas b/unicode/src/base/UPlatformWindows.pas index e198958a..2d0bd212 100644 --- a/unicode/src/base/UPlatformWindows.pas +++ b/unicode/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,62 +61,10 @@ 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; @@ -151,6 +97,7 @@ begin Code := GetFileAttributesW(PWideChar(Directory)); Result := (Code <> -1) and (FILE_ATTRIBUTE_DIRECTORY and Code <> 0); end; +*) //------------------------------ //Start more than One Time Prevention @@ -180,41 +127,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 +137,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/unicode/src/base/UPlaylist.pas b/unicode/src/base/UPlaylist.pas index cc983a9e..09c196ef 100644 --- a/unicode/src/base/UPlaylist.pas +++ b/unicode/src/base/UPlaylist.pas @@ -34,7 +34,9 @@ interface {$I switches.inc} uses + Classes, USong, + UPath, UPathUtils; type @@ -47,8 +49,8 @@ type APlaylistItem = array of TPlaylistItem; TPlaylist = record - Name: string; - Filename: String; + Name: UTF8String; + Filename: IPath; Items: APlaylistItem; end; @@ -68,20 +70,20 @@ type Playlists: APlaylist; constructor Create; - Procedure LoadPlayLists; - Function LoadPlayList(Index: Cardinal; const 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(const 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: @@ -95,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 @@ -117,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; const 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: UTF8String): 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 := TTextFileStream.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 @@ -224,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; @@ -233,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 := TTextFileStream.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; @@ -324,31 +327,30 @@ end; //---------- //AddPlaylist - Adds a Playlist and Returns the Index //---------- -Function TPlayListManager.AddPlaylist(const 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; 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; //Save new Playlist SavePlayList(Result); @@ -357,28 +359,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 @@ -471,7 +473,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/unicode/src/base/USkins.pas b/unicode/src/base/USkins.pas index a19b7546..6ef5c596 100644 --- a/unicode/src/base/USkins.pas +++ b/unicode/src/base/USkins.pas @@ -33,31 +33,34 @@ interface {$I switches.inc} +uses + UPath; + type TSkinTexture = record Name: string; - FileName: string; + FileName: IPath; end; TSkinEntry = record Theme: string; Name: string; - Path: string; - FileName: string; + Path: IPath; + FileName: IPath; Creator: string; // not used yet end; TSkin = class Skin: array of TSkinEntry; SkinTexture: array of TSkinTexture; - SkinPath: string; + 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; @@ -74,7 +77,8 @@ uses UIni, ULog, UMain, - UPathUtils; + UPathUtils, + UFileSystem; constructor TSkin.Create; begin @@ -86,45 +90,43 @@ end; procedure TSkin.LoadList; var - SR: TSearchRec; + Iter: IFileIterator; + DirInfo: TFileInfo; begin - if FindFirst(SkinsPath+'*', faDirectory, SR) = 0 then + Iter := FileSystem.FileFind(SkinsPath.Append('*'), faDirectory); + while Iter.HasNext do begin - repeat - if (SR.Name <> '.') and (SR.Name <> '..') then - ParseDir(SkinsPath + SR.Name + PathDelim); - until FindNext(SR) <> 0; - end; // if - FindClose(SR); + 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 + Iter := FileSystem.FileFind(Dir.Append('*.ini'), 0); + while Iter.HasNext do begin - repeat - - if (SR.Name <> '.') and (SR.Name <> '..') then - LoadHeader(Dir + SR.Name); - - until FindNext(SR) <> 0; + 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; 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', ''); @@ -142,7 +144,7 @@ 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); @@ -151,30 +153,29 @@ 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; 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'); diff --git a/unicode/src/base/USong.pas b/unicode/src/base/USong.pas index 86349359..203ff517 100644 --- a/unicode/src/base/USong.pas +++ b/unicode/src/base/USong.pas @@ -57,7 +57,9 @@ uses {$ENDIF} UCatCovers, UXMLSong, - UTextEncoding; + UTextEncoding, + UFilesystem, + UPath; type @@ -74,6 +76,7 @@ type end; TSong = class + private FileLineNo : integer; // line, which is read last, for error reporting function EncodeFilename(Filename: string): string; @@ -81,19 +84,22 @@ type 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 ReadTXTHeader(const aFileName: IPath): 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: WideString; - Mp3: WideString; - Background: WideString; - Video: WideString; + Cover: IPath; + Mp3: IPath; + Background: IPath; + Video: IPath; // sorting methods Genre: UTF8String; @@ -127,7 +133,7 @@ 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 + //SongFile: TextFile; // all procedures in this unit operate on this file Base : array[0..1] of integer; Rel : array[0..1] of integer; @@ -140,7 +146,7 @@ type constructor Create(); overload; - constructor Create( const aFileName : WideString ); overload; + constructor Create(const aFileName : IPath); overload; function LoadSong: boolean; function LoadXMLSong: boolean; function Analyse(): boolean; @@ -167,58 +173,61 @@ begin inherited; 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 GetFolderCategory: WideString; - var - I: Integer; - P: Integer; //position of next path delimiter - begin - Result := 'Unknown'; //default folder category, if we can't locate the song dir +// 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 - if (AnsiStartsText(SongPaths.Strings[I], aFilename)) then + 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 - P := PosEx(PathDelim, aFilename, Length(SongPaths.Strings[I]) + 1); - - If (P > 0) then - begin - // we have found the category name => get it - Result := copy(self.Path, Length(SongPaths.Strings[I]) + 1, P - Length(SongPaths.Strings[I]) - 1); - end - else - begin - // songs are in the "root" of the songdir => use songdir for the categorys name - Result := SongPaths.Strings[I]; - end; - - Exit; + // songs are in the "root" of the songdir => use songdir for the categorys name + Result := CurSongPath.ToUTF8; // TODO: remove trailing path-delim? + 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 := GetFolderCategory; - self.FileName := ExtractFileName( aFileName ); - (* - if ReadTXTHeader( aFileName ) then + if ReadTXTHeader(aFileName) then begin LoadSong(); end @@ -227,8 +236,21 @@ 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.EncodeFilename(Filename: string): string; @@ -247,7 +269,7 @@ end; //Load TXT Song function TSong.LoadSong(): boolean; var - TempC: char; + TempC: AnsiChar; Text: UTF8String; Count: integer; Both: boolean; @@ -256,15 +278,19 @@ var Param3: integer; ParamS: UTF8String; I: integer; + NotesFound: boolean; + TextStream: 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 @@ -275,34 +301,42 @@ begin if Length(Player) = 2 then Both := true; + {$MESSAGE warn 'TODO: TSong.LoadSong'} + + (* try // Open song file for reading..... - FileMode := fmOpenRead; - AssignFile(SongFile, fFileName); - Reset(SongFile); + TextStream := nil; + TextStream := TBinaryFileStream.Create(FullFileName, fmOpenRead); //Clear old Song Header - if (self.Path = '') then - self.Path := ExtractFilePath(FileName); + if (Self.Path.IsUnset) then + Self.Path := FullFileName.GetPath(); - if (self.FileName = '') then - self.Filename := ExtractFileName(FileName); + if (Self.FileName.IsUnset) then + Self.Filename := FullFileName.GetName(); - FileLineNo := 0; //Search for Note Begining - repeat - ReadLn(SongFile, Text); + FileLineNo := 0; + NotesFound := false; + while (TextStream.ReadLine(Text)) do + begin Inc(FileLineNo); - - if (Eof(SongFile)) then - begin //Song File Corrupted - No Notes - CloseFile(SongFile); - Log.LogError('Could not load txt File, no Notes found: ' + FileName); - LastError := 'ERROR_CORRUPT_SONG_NO_NOTES'; - Exit; + if (Length(Text) > 0) and (Text[0] in [':', 'F', '*']) then + begin + TempC := Text[0]; + NotesFound := true; + Break; end; - Read(SongFile, TempC); - until ((TempC = ':') or (TempC = 'F') or (TempC = '*')); + end; + + if (not NotesFound) then + begin //Song File Corrupted - No Notes + TextStream.Free; + Log.LogError('Could not load txt File, no Notes found: ' + FileName); + LastError := 'ERROR_CORRUPT_SONG_NO_NOTES'; + Exit; + end; SetLength(Lines, 2); for Count := 0 to High(Lines) do @@ -327,7 +361,7 @@ begin while (TempC <> 'E') and (not EOF(SongFile)) do begin - if (TempC = ':') or (TempC = '*') or (TempC = 'F') then + if (TempC in [':', '*', 'F']) then begin // read notes Read(SongFile, Param1); @@ -398,7 +432,7 @@ 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: "' + fFileName + '"'); + Log.LogError('Error Loading File, Can''t find any Linebreaks: "' + FullFileName + '"'); exit; end; @@ -425,10 +459,11 @@ begin end; LastError := 'ERROR_CORRUPT_SONG_ERROR_IN_LINE'; - Log.LogError('Error Loading File: "' + fFileName + '" in Line ' + inttostr(FileLineNo)); + Log.LogError('Error Loading File: "' + FullFileName + '" in Line ' + inttostr(FileLineNo)); exit; end; - + *) + Result := true; end; @@ -447,13 +482,15 @@ 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; @@ -491,7 +528,7 @@ begin //Try to Parse the Song - if Parser.ParseSong(Path + PathDelim + FileName) then + if Parser.ParseSong(FileNamePath) then begin //Writeln('XML Inputfile Parsed succesful'); @@ -558,7 +595,7 @@ begin end else begin - Log.LogError('Could not parse Inputfile: ' + Path + PathDelim + FileName); + Log.LogError('Could not parse Inputfile: ' + FileNamePath.ToNative); exit; end; @@ -570,10 +607,11 @@ begin Result := true; end; -function TSong.ReadXMLHeader(const aFileName : WideString): boolean; +function TSong.ReadXMLHeader(const aFileName : IPath): boolean; var Done : byte; Parser : TParser; + FileNamePath: IPath; begin Result := true; Done := 0; @@ -582,7 +620,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 @@ -601,9 +640,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 @@ -624,10 +663,10 @@ 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 @@ -648,7 +687,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; @@ -657,36 +696,35 @@ 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; -function TSong.ReadTXTHeader(const aFileName : WideString): boolean; - - {** - * "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; +{** + * "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 : IPath): boolean; var Line, Identifier: string; Value: string; @@ -697,6 +735,10 @@ begin Result := true; Done := 0; + {$message warn 'TSong.ReadTXTHeader'} + + (* + //Read first Line ReadLn (SongFile, Line); @@ -913,7 +955,7 @@ begin else //unknown Error Log.LogError('File Incomplete or not Ultrastar TxT (B - '+ inttostr(Done) +'): ' + aFileName); end; - + *) end; function TSong.GetErrorLineNo: integer; @@ -1034,7 +1076,8 @@ begin end else 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); + 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; @@ -1076,7 +1119,7 @@ begin Encoding := DEFAULT_ENCODING; //Required Information - Mp3 := ''; + Mp3 := PATH_NONE; SetLength(BPM, 0); GAP := 0; @@ -1084,9 +1127,9 @@ begin Finish := 0; //Additional Information - Background := ''; - Cover := ''; - Video := ''; + Background := PATH_NONE; + Cover := PATH_NONE; + Video := PATH_NONE; VideoGAP := 0; NotesGAP := 0; Resolution := 4; @@ -1096,7 +1139,8 @@ begin end; function TSong.Analyse(): boolean; - +var + SongFile: TTextFileStream; begin Result := false; @@ -1104,20 +1148,17 @@ begin FileLineNo := 0; //Open File and set File Pointer to the beginning - AssignFile(SongFile, self.Path + self.FileName); - + SongFile := TTextFileStream.Create(Self.Path.Append(Self.FileName), fmOpenRead); try - Reset(SongFile); - //Clear old Song Header - self.clear; + Self.clear; //Read Header - Result := self.ReadTxTHeader( FileName ) + Result := self.ReadTxTHeader(FileName) //And Close File finally - CloseFile(SongFile); + SongFile.Free; end; end; diff --git a/unicode/src/base/USongs.pas b/unicode/src/base/USongs.pas index 2b2bcf49..55ddc5e9 100644 --- a/unicode/src/base/USongs.pas +++ b/unicode/src/base/USongs.pas @@ -40,28 +40,26 @@ 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; @@ -107,11 +105,10 @@ type procedure LoadSongList; // load all songs - procedure BrowseDir(Dir: widestring); // should return number of songs in the future - procedure BrowseTXTFiles(Dir: widestring); - procedure BrowseXMLFiles(Dir: widestring); + procedure 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; end; @@ -165,6 +162,7 @@ uses UIni, UPathUtils, UNote, + UFilesystem, UUnicodeUtils; constructor TSongs.Create(); @@ -239,7 +237,7 @@ begin // browse directories for I := 0 to SongPaths.Count-1 do - BrowseDir(SongPaths[I]); + BrowseDir(SongPaths[I] as IPath); if assigned(CatSongs) then CatSongs.Refresh; @@ -271,13 +269,13 @@ begin Resume(); end; -procedure TSongs.BrowseDir(Dir: widestring); +procedure TSongs.BrowseDir(Dir: IPath); begin BrowseTXTFiles(Dir); BrowseXMLFiles(Dir); end; -procedure TSongs.BrowseTXTFiles(Dir: widestring); +procedure TSongs.BrowseTXTFiles(Dir: IPath); var i: integer; Files: TDirectoryEntryArray; @@ -285,27 +283,27 @@ var begin try - Files := Platform.DirectoryFindFiles(Dir, '.txt', true) + Files := DirectoryFindFiles(Dir, '.txt', true) except - Log.LogError('Couldn''t deal with directory/file: ' + Dir + ' in TSongs.BrowseTXTFiles') + Log.LogError('Couldn''t deal with directory/file: ' + Dir.ToNative + ' in TSongs.BrowseTXTFiles') end; for i := 0 to Length(Files) - 1 do begin if Files[i].IsDirectory then begin - BrowseTXTFiles(Dir + Files[i].Name + PathDelim); //Recursive Call + BrowseTXTFiles(Dir.Append(Files[i].Name, pdAppend)); //Recursive Call end else begin - lSong := TSong.create(Dir + Files[i].Name); + lSong := TSong.Create(Dir.Append(Files[i].Name)); if lSong.Analyse then SongList.add(lSong) else begin - Log.LogError('AnalyseFile failed for "' + Files[i].Name + '".'); - freeandnil(lSong); + Log.LogError('AnalyseFile failed for "' + Files[i].Name.ToNative + '".'); + FreeAndNil(lSong); end; end; @@ -314,7 +312,7 @@ begin end; -procedure TSongs.BrowseXMLFiles(Dir: widestring); +procedure TSongs.BrowseXMLFiles(Dir: IPath); var i: integer; Files: TDirectoryEntryArray; @@ -322,26 +320,26 @@ var begin try - Files := Platform.DirectoryFindFiles(Dir, '.xml', true) + Files := DirectoryFindFiles(Dir, '.xml', true) except - Log.LogError('Couldn''t deal with directory/file: ' + Dir + ' in TSongs.BrowseXMLFiles') + Log.LogError('Couldn''t deal with directory/file: ' + Dir.ToNative + ' in TSongs.BrowseXMLFiles') end; for i := 0 to Length(Files) - 1 do begin if Files[i].IsDirectory then begin - BrowseXMLFiles(Dir + Files[i].Name + PathDelim); // Recursive Call + BrowseXMLFiles(Dir.Append(Files[i].Name, pdAppend)); // Recursive Call end else begin - lSong := TSong.create(Dir + Files[i].Name); + lSong := TSong.Create(Dir.Append(Files[i].Name)); if lSong.AnalyseXML then SongList.add(lSong) else begin - Log.LogError('AnalyseFile failed for "' + Files[i].Name + '".'); + Log.LogError('AnalyseFile failed for "' + Files[i].Name.ToNative + '".'); freeandnil(lSong); end; @@ -377,7 +375,7 @@ 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; @@ -419,18 +417,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 @@ -603,7 +589,7 @@ begin end; sFolder: begin - if (CompareText(CurCategory, CurSong.Folder) <> 0) then + if (UTF8CompareText(CurCategory, CurSong.Folder) <> 0) then begin CurCategory := CurSong.Folder; // add folder tab @@ -831,7 +817,7 @@ begin begin case Filter of fltAll: - TmpString := Song[I].Artist + ' ' + Song[i].Title + ' ' + UTF8Encode(Song[i].Folder); + TmpString := Song[I].Artist + ' ' + Song[i].Title + ' ' + Song[i].Folder; fltTitle: TmpString := Song[I].Title; fltArtist: diff --git a/unicode/src/base/UTextEncoding.pas b/unicode/src/base/UTextEncoding.pas index 966064c0..a0d455dd 100644 --- a/unicode/src/base/UTextEncoding.pas +++ b/unicode/src/base/UTextEncoding.pas @@ -1,239 +1,239 @@ -{* 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/menu/UMenuText.pas $ - * $Id: UMenuText.pas 1485 2008-10-28 20:16:05Z tobigun $ - *} - -unit UTextEncoding; - -interface - -{$IFDEF FPC} - {$MODE Delphi} -{$ENDIF} - -{$I switches.inc} - -uses - SysUtils; - -type - 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) - ); - -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: AnsiString; out Dst: WideString; SrcEncoding: TEncoding): boolean; overload; -function DecodeString(const Src: AnsiString; SrcEncoding: TEncoding): WideString; overload; -function DecodeStringUTF8(const Src: AnsiString; out Dst: UTF8String; SrcEncoding: TEncoding): boolean; overload; -function DecodeStringUTF8(const Src: AnsiString; 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: AnsiString; DstEncoding: TEncoding): boolean; overload; -function EncodeString(const Src: WideString; DstEncoding: TEncoding): AnsiString; overload; -function EncodeStringUTF8(const Src: UTF8String; out Dst: AnsiString; DstEncoding: TEncoding): boolean; overload; -function EncodeStringUTF8(const Src: UTF8String; DstEncoding: TEncoding): AnsiString; overload; - -{** - * If Text starts with an UTF-8 BOM, the BOM is removed and true will - * be returned. - *} -function CheckReplaceUTF8BOM(var Text: AnsiString): boolean; - -{** - * 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, - UUnicodeUtils; - -type - IEncoder = interface - function GetName(): AnsiString; - function Encode(const InStr: UCS4String; out OutStr: AnsiString): boolean; - function Decode(const InStr: AnsiString; out OutStr: UCS4String): boolean; - end; - - TEncoder = class(TInterfacedObject, IEncoder) - public - function GetName(): AnsiString; virtual; abstract; - function Encode(const InStr: UCS4String; out OutStr: AnsiString): boolean; virtual; abstract; - function Decode(const InStr: AnsiString; out OutStr: UCS4String): boolean; virtual; abstract; - end; - - TSingleByteEncoder = class(TEncoder) - public - function Encode(const InStr: UCS4String; out OutStr: AnsiString): boolean; override; - function Decode(const InStr: AnsiString; 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 - ERROR_CHAR = '?'; - -var - Encoders: array[TEncoding] of IEncoder; - -function TSingleByteEncoder.Encode(const InStr: UCS4String; out OutStr: AnsiString): 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 TSingleByteEncoder.Decode(const InStr: AnsiString; out OutStr: UCS4String): boolean; -var - I: integer; -begin - SetLength(OutStr, Length(InStr)+1); - Result := true; - for I := 1 to Length(InStr) do - begin - if (not DecodeChar(InStr[I], OutStr[I-1])) then - Result := false; - end; - OutStr[High(OutStr)] := 0; -end; - -function DecodeString(const Src: AnsiString; out Dst: WideString; SrcEncoding: TEncoding): boolean; -var - DstUCS4: UCS4String; -begin - Result := Encoders[SrcEncoding].Decode(Src, DstUCS4); - Dst := UCS4StringToWideString(DstUCS4); -end; - -function DecodeString(const Src: AnsiString; SrcEncoding: TEncoding): WideString; -begin - DecodeString(Src, Result, SrcEncoding); -end; - -function DecodeStringUTF8(const Src: AnsiString; out Dst: UTF8String; SrcEncoding: TEncoding): boolean; -var - DstUCS4: UCS4String; -begin - Result := Encoders[SrcEncoding].Decode(Src, DstUCS4); - Dst := UCS4ToUTF8String(DstUCS4); -end; - -function DecodeStringUTF8(const Src: AnsiString; SrcEncoding: TEncoding): UTF8String; -begin - DecodeStringUTF8(Src, Result, SrcEncoding); -end; - -function EncodeString(const Src: WideString; out Dst: AnsiString; DstEncoding: TEncoding): boolean; -begin - Result := Encoders[DstEncoding].Encode(WideStringToUCS4String(Src), Dst); -end; - -function EncodeString(const Src: WideString; DstEncoding: TEncoding): AnsiString; -begin - EncodeString(Src, Result, DstEncoding); -end; - -function EncodeStringUTF8(const Src: UTF8String; out Dst: AnsiString; DstEncoding: TEncoding): boolean; -begin - Result := Encoders[DstEncoding].Encode(UTF8ToUCS4String(Src), Dst); -end; - -function EncodeStringUTF8(const Src: UTF8String; DstEncoding: TEncoding): AnsiString; -begin - EncodeStringUTF8(Src, Result, DstEncoding); -end; - -function CheckReplaceUTF8BOM(var Text: AnsiString): 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 - Result := Encoding; - Exit; +{* 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/menu/UMenuText.pas $ + * $Id: UMenuText.pas 1485 2008-10-28 20:16:05Z tobigun $ + *} + +unit UTextEncoding; + +interface + +{$IFDEF FPC} + {$MODE Delphi} +{$ENDIF} + +{$I switches.inc} + +uses + SysUtils, + UUnicodeUtils; + +type + 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) + ); + +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; + +{** + * 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; + +type + 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 + ERROR_CHAR = '?'; + +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 TSingleByteEncoder.Decode(const InStr: RawByteString; out OutStr: UCS4String): boolean; +var + I: integer; +begin + SetLength(OutStr, Length(InStr)+1); + Result := true; + for I := 1 to Length(InStr) do + begin + 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 + Result := Encoding; + Exit; end; end; - Result := Default; -end; - -function EncodingName(Encoding: TEncoding): AnsiString; -begin - Result := Encoders[Encoding].GetName(); -end; - -{$I ../encoding/Locale.inc} -{$I ../encoding/UTF8.inc} -{$I ../encoding/CP1250.inc} -{$I ../encoding/CP1252.inc} - -initialization - Encoders[encLocale] := TEncoderLocale.Create; - Encoders[encUTF8] := TEncoderUTF8.Create; - Encoders[encCP1250] := TEncoderCP1250.Create; - Encoders[encCP1252] := TEncoderCP1252.Create; - -end. + Result := Default; +end; + +function EncodingName(Encoding: TEncoding): AnsiString; +begin + Result := Encoders[Encoding].GetName(); +end; + +{$I ../encoding/Locale.inc} +{$I ../encoding/UTF8.inc} +{$I ../encoding/CP1250.inc} +{$I ../encoding/CP1252.inc} + +initialization + Encoders[encLocale] := TEncoderLocale.Create; + Encoders[encUTF8] := TEncoderUTF8.Create; + Encoders[encCP1250] := TEncoderCP1250.Create; + Encoders[encCP1252] := TEncoderCP1252.Create; + +end. diff --git a/unicode/src/base/UTexture.pas b/unicode/src/base/UTexture.pas index 97f244fe..04badae4 100644 --- a/unicode/src/base/UTexture.pas +++ b/unicode/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,7 +92,7 @@ procedure AdjustPixelFormat(var TexSurface: PSDL_Surface; Typ: TTextureType); type PTextureEntry = ^TTextureEntry; TTextureEntry = record - Name: string; + Name: IPath; Typ: TTextureType; Color: cardinal; @@ -105,7 +106,7 @@ type 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; + function FindTexture(const Name: IPath; Typ: TTextureType; Color: cardinal): integer; end; TTextureUnit = class @@ -116,14 +117,14 @@ type 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; + 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; @@ -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; @@ -235,18 +236,18 @@ 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: integer; @@ -260,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; @@ -363,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); @@ -413,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; @@ -467,12 +468,12 @@ 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; diff --git a/unicode/src/base/UThemes.pas b/unicode/src/base/UThemes.pas index 3fd77853..11ec746c 100644 --- a/unicode/src/base/UThemes.pas +++ b/unicode/src/base/UThemes.pas @@ -34,11 +34,12 @@ interface {$I switches.inc} uses - ULog, IniFiles, SysUtils, Classes, - UTexture; + ULog, + UTexture, + UPath; type TRGB = record @@ -531,10 +532,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 @@ -761,11 +762,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; @@ -845,12 +846,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(); @@ -893,7 +894,7 @@ begin end; -function TTheme.LoadTheme(FileName: string; sColor: integer): boolean; +function TTheme.LoadTheme(const FileName: IPath; sColor: integer): boolean; var I: integer; begin @@ -901,23 +902,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 diff --git a/unicode/src/base/UUnicodeUtils.pas b/unicode/src/base/UUnicodeUtils.pas index ce1731b6..29be778a 100644 --- a/unicode/src/base/UUnicodeUtils.pas +++ b/unicode/src/base/UUnicodeUtils.pas @@ -148,6 +148,11 @@ function UCS4UpperCase(const str: UCS4String): UCS4String; overload; *} 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). *} @@ -520,6 +525,11 @@ begin 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)); diff --git a/unicode/src/base/UXMLSong.pas b/unicode/src/base/UXMLSong.pas index 58b48789..e8962539 100644 --- a/unicode/src/base/UXMLSong.pas +++ b/unicode/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,28 @@ 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 + {$message warn 'TODO: TParser.ParseSong'} + +(* Result := False; - if FileExists(Filename) then + if Filename.IsFile() then begin SSFile := TStringList.Create; + ErrorMessage := 'Can''t open melody.xml file'; + try - ErrorMessage := 'Can''t open melody.xml file'; - SSFile.LoadFromFile(Filename); + FileStream := TBinaryFileStream.Create(Filename); + SSFile.LoadFromStream(FileStream); + ErrorMessage := ''; Result := True; + I := 0; SongInfo.CountSentences := 0; @@ -153,7 +167,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]); @@ -164,12 +178,16 @@ begin SSFile.Free; end; end; + *) end; -Function TParser.ParseSongHeader (const Filename: String): Boolean; +function TParser.ParseSongHeader (const Filename: IPath): Boolean; var I: Integer; begin Result := False; + + {$message warn 'TODO: TParser.ParseSong'} + (* if FileExists(Filename) then begin SSFile := TStringList.Create; @@ -211,6 +229,7 @@ begin end else ErrorMessage := 'Can''t find melody.xml file'; + *) end; Function TParser.ParseLine(Line: String): Boolean; @@ -569,7 +588,7 @@ begin Result := true; end; -Function TParser.ParseConfigforEdition(const Filename: String): String; +Function TParser.ParseConfigForEdition(const Filename: IPath): String; var txt: TStringlist; I: Integer; @@ -578,6 +597,10 @@ var begin Result := ''; txt := TStringlist.Create; + + {$message warn 'TODO: TParser.ParseConfigForEdition'} + + (* try txt.LoadFromFile(Filename); @@ -601,6 +624,7 @@ begin finally txt.Free; end; + *) end; end. diff --git a/unicode/src/lib/midi/MidiFile.pas b/unicode/src/lib/midi/MidiFile.pas index 6b33c900..acf44c04 100644 --- a/unicode/src/lib/midi/MidiFile.pas +++ b/unicode/src/lib/midi/MidiFile.pas @@ -97,13 +97,13 @@ interface 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/unicode/src/media/UAudioCore_Bass.pas b/unicode/src/media/UAudioCore_Bass.pas index 12623dc1..197f9760 100644 --- a/unicode/src/media/UAudioCore_Bass.pas +++ b/unicode/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/unicode/src/media/UAudioDecoder_Bass.pas b/unicode/src/media/UAudioDecoder_Bass.pas index 6bbdaeaa..d6d2425a 100644 --- a/unicode/src/media/UAudioDecoder_Bass.pas +++ b/unicode/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) @@ -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 @@ -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, PAnsiChar(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/unicode/src/media/UAudioDecoder_FFmpeg.pas b/unicode/src/media/UAudioDecoder_FFmpeg.pas index 97d8a8df..aed4390c 100644 --- a/unicode/src/media/UAudioDecoder_FFmpeg.pas +++ b/unicode/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, Math, - UMusic, - UIni, - UMain, + SysUtils, avcodec, avformat, avutil, avio, mathematics, // used for av_rescale_q rational, - SDL, - SysUtils, + UMusic, + UIni, + UMain, UMediaCore_FFmpeg, ULog, UCommon, - UConfig; + UConfig, + UPath; const MAX_AUDIOQ_SIZE = (5 * 16 * 1024); @@ -138,7 +139,7 @@ type AudioBufferSize: integer; AudioBuffer: PByteArray; - Filename: string; + Filename: IPath; procedure SetPositionIntern(Time: real; Flush: boolean; Blocking: boolean); procedure SetEOF(State: boolean); {$IFDEF HasInline}inline;{$ENDIF} @@ -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; @@ -183,7 +184,7 @@ type 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, PAnsiChar(Filename), nil, 0, nil) <> 0) then + // open audio file (TODO: unicode support) + if (av_open_input_file(FormatCtx, PAnsiChar(Filename.ToNative), 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, PAnsiChar(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; @@ -1117,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/unicode/src/media/UAudioInput_Bass.pas b/unicode/src/media/UAudioInput_Bass.pas index ad6c3818..9d4417f1 100644 --- a/unicode/src/media/UAudioInput_Bass.pas +++ b/unicode/src/media/UAudioInput_Bass.pas @@ -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/unicode/src/media/UAudioPlaybackBase.pas b/unicode/src/media/UAudioPlaybackBase.pas index 7d143fdc..de2d5563 100644 --- a/unicode/src/media/UAudioPlaybackBase.pas +++ b/unicode/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/unicode/src/media/UAudioPlayback_Bass.pas b/unicode/src/media/UAudioPlayback_Bass.pas index 923c1d7b..1d7a44dc 100644 --- a/unicode/src/media/UAudioPlayback_Bass.pas +++ b/unicode/src/media/UAudioPlayback_Bass.pas @@ -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/unicode/src/media/UMedia_dummy.pas b/unicode/src/media/UMedia_dummy.pas index 7558dd0b..25e94724 100644 --- a/unicode/src/media/UMedia_dummy.pas +++ b/unicode/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/unicode/src/media/UVideo.pas b/unicode/src/media/UVideo.pas index f55690b2..de01fd68 100644 --- a/unicode/src/media/UVideo.pas +++ b/unicode/src/media/UVideo.pas @@ -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} @@ -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; @@ -252,7 +253,7 @@ begin 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,10 @@ begin Reset(); - errnum := av_open_input_file(fFormatContext, PChar(aFileName), nil, 0, nil); + errnum := av_open_input_file(fFormatContext, PChar(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; diff --git a/unicode/src/menu/UDisplay.pas b/unicode/src/menu/UDisplay.pas index a4d16500..aded7f3f 100644 --- a/unicode/src/menu/UDisplay.pas +++ b/unicode/src/menu/UDisplay.pas @@ -36,10 +36,11 @@ interface uses UCommon, SDL, - UMenu, gl, glu, - SysUtils; + SysUtils, + UMenu, + UPath; type TDisplay = class @@ -504,47 +505,47 @@ end; procedure TDisplay.SaveScreenShot; var Num: integer; - FileName: string; + FileName: IPath; + Prefix: UTF8String; ScreenData: PChar; Surface: PSDL_Surface; Success: boolean; Align: integer; RowSize: integer; begin -// Exit if Screenshot-path does not exist or read-only - if (ScreenshotsPath = '') then + // Exit if Screenshot-path does not exist or read-only + 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 + // we must take the row-alignment (4byte by default) into account glGetIntegerv(GL_PACK_ALIGNMENT, @Align); -// calc aligned row-size + // calc aligned row-size RowSize := ((ScreenW*3 + (Align-1)) div Align) * Align; 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)) + ScreenPopupError.ShowPopup('Screenshot saved: ' + FileName.GetName.ToUTF8()) else ScreenPopupError.ShowPopup('Screenshot failed'); @@ -559,7 +560,7 @@ procedure TDisplay.DrawDebugInformation; 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); @@ -571,13 +572,13 @@ begin glEnd; glDisable(GL_BLEND); -// set font specs + // set font specs SetFontStyle(0); SetFontSize(21); SetFontItalic(false); glColor4f(0, 0, 0, 1); -// calculate fps + // calculate fps Ticks := SDL_GetTicks(); if (Ticks >= NextFPSSwap) then begin @@ -588,17 +589,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/unicode/src/menu/UMenu.pas b/unicode/src/menu/UMenu.pas index f7e98d32..6c01451c 100644 --- a/unicode/src/menu/UMenu.pas +++ b/unicode/src/menu/UMenu.pas @@ -38,6 +38,7 @@ uses Math, gl, SDL, + UPath, UMenuBackground, UMenuButton, UMenuButtonCollection, @@ -96,13 +97,13 @@ 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; @@ -113,9 +114,9 @@ type // 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: UTF8String); overload; procedure AddButtonText(AddX, AddY: real; ColR, ColG, ColB: real; const AddText: UTF8String); overload; @@ -123,16 +124,16 @@ type 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; const 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 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 string; var Data: integer); + 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); @@ -389,7 +390,7 @@ begin 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) @@ -597,29 +598,29 @@ 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; + 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; + 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; + const TexName: IPath; Typ: TTextureType): integer; var StatNum: integer; @@ -627,7 +628,7 @@ 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; @@ -640,26 +641,26 @@ end; function TMenu.AddStatic(X, Y, W, H: real; ColR, ColG, ColB: real; - const Name: string; + 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; + 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; + const TexName: IPath; Typ: TTextureType; Color: integer; Reflection: boolean; @@ -675,11 +676,11 @@ 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 @@ -841,18 +842,18 @@ 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; + const TexName: IPath; Typ: TTextureType; Reflection: boolean; ReflectionSpacing, DeSelectReflectionSpacing: real): integer; @@ -874,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 @@ -1264,7 +1265,7 @@ begin end; end; -function TMenu.AddSelectSlide(ThemeSelectS: TThemeSelectSlide; var Data: integer; const Values: array of string): integer; +function TMenu.AddSelectSlide(ThemeSelectS: TThemeSelectSlide; var Data: integer; const Values: array of UTF8String): integer; var SO: integer; begin @@ -1301,7 +1302,7 @@ 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 TexName: IPath; Typ: TTextureType; const SBGName: IPath; SBGTyp: TTextureType; const Caption: UTF8String; var Data: integer): integer; var S: integer; @@ -1312,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; @@ -1453,7 +1454,8 @@ begin } end; -procedure TMenu.UpdateSelectSlideOptions(ThemeSelectSlide: TThemeSelectSlide; SelectNum: integer; const 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 diff --git a/unicode/src/menu/UMenuBackgroundColor.pas b/unicode/src/menu/UMenuBackgroundColor.pas index a5c2a70a..45b58c1e 100644 --- a/unicode/src/menu/UMenuBackgroundColor.pas +++ b/unicode/src/menu/UMenuBackgroundColor.pas @@ -1,73 +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, - 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; - +{* 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/unicode/src/menu/UMenuBackgroundFade.pas b/unicode/src/menu/UMenuBackgroundFade.pas index b61a4542..6fef063f 100644 --- a/unicode/src/menu/UMenuBackgroundFade.pas +++ b/unicode/src/menu/UMenuBackgroundFade.pas @@ -36,7 +36,8 @@ interface uses UThemes, UTexture, - UMenuBackground; + UMenuBackground, + UPath; //TMenuBackgroundFade - Background Fade In for Overlay screens //-------- @@ -72,7 +73,7 @@ uses constructor TMenuBackgroundFade.Create(const ThemedSettings: TThemeBackground); var - texFilename: string; + texFilename: IPath; begin inherited; FadeTime := 0; @@ -82,7 +83,6 @@ begin 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); diff --git a/unicode/src/menu/UMenuBackgroundNone.pas b/unicode/src/menu/UMenuBackgroundNone.pas index 1fccc007..c64f3023 100644 --- a/unicode/src/menu/UMenuBackgroundNone.pas +++ b/unicode/src/menu/UMenuBackgroundNone.pas @@ -1,70 +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, - 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; - +{* 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/unicode/src/menu/UMenuBackgroundTexture.pas b/unicode/src/menu/UMenuBackgroundTexture.pas index a1b9e88a..91ea6503 100644 --- a/unicode/src/menu/UMenuBackgroundTexture.pas +++ b/unicode/src/menu/UMenuBackgroundTexture.pas @@ -36,7 +36,8 @@ interface uses UThemes, UTexture, - UMenuBackground; + UMenuBackground, + UPath; //TMenuBackgroundColor - Background Color //-------- @@ -65,7 +66,8 @@ uses UGraphic; constructor TMenuBackgroundTexture.Create(const ThemedSettings: TThemeBackground); -var texFilename: string; +var + texFilename: IPath; begin inherited; @@ -75,7 +77,6 @@ begin Color := ThemedSettings.Color; texFilename := Skin.GetTextureFileName(ThemedSettings.Tex); - texFilename := AdaptFilePaths(texFilename); Tex := Texture.GetTexture(texFilename, TEXTURE_TYPE_PLAIN); if (Tex.TexNum = 0) then diff --git a/unicode/src/menu/UMenuBackgroundVideo.pas b/unicode/src/menu/UMenuBackgroundVideo.pas index d1ce0f09..0e0a2bff 100644 --- a/unicode/src/menu/UMenuBackgroundVideo.pas +++ b/unicode/src/menu/UMenuBackgroundVideo.pas @@ -36,7 +36,8 @@ interface uses UThemes, UMenuBackground, - UVideo; + UVideo, + UPath; //TMenuBackgroundColor - Background Color //-------- @@ -82,7 +83,7 @@ type type } TMenuBackgroundVideo = class (TMenuBackground) private - fFilename: string; + fFilename: IPath; public constructor Create(const ThemedSettings: TThemeBackground); override; procedure OnShow; override; @@ -115,15 +116,13 @@ begin raise EMenuBackgroundError.Create('TMenuBackgroundVideo: No video filename present'); fFileName := Skin.GetTextureFileName(ThemedSettings.Tex); - fFileName := AdaptFilePaths( fFileName ); - - if fileexists(fFilename) AND VideoPlayback.Open( fFileName ) then + 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); + raise EMenuBackgroundError.Create('TMenuBackgroundVideo: Can''t load background video: ' + fFilename.ToNative); end; destructor TMenuBackgroundVideo.Destroy; diff --git a/unicode/src/screens/UScreenCredits.pas b/unicode/src/screens/UScreenCredits.pas index af1eea53..4e1dc5f1 100644 --- a/unicode/src/screens/UScreenCredits.pas +++ b/unicode/src/screens/UScreenCredits.pas @@ -35,15 +35,16 @@ interface uses SysUtils, - UMenu, SDL, SDL_Image, + gl, + UMenu, UDisplay, UTexture, - gl, UMusic, UFiles, UThemes, + UPath, UGraphicClasses; type @@ -204,38 +205,38 @@ 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; @@ -255,7 +256,7 @@ begin deluxe_slidein := 0; Credits_Alpha := 0; // Music.SetLoop(true); loop loops not, shit - AudioPlayback.Open(soundpath + 'wome-credits-tune.mp3'); // thank you wetue + AudioPlayback.Open(soundpath.Append('wome-credits-tune.mp3')); // thank you wetue // Music.Play; CTime := 0; // setlength(CTime_hold,0); @@ -1386,7 +1387,7 @@ begin 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.Play; diff --git a/unicode/src/screens/UScreenEditConvert.pas b/unicode/src/screens/UScreenEditConvert.pas index 5dfa3b43..28182097 100644 --- a/unicode/src/screens/UScreenEditConvert.pas +++ b/unicode/src/screens/UScreenEditConvert.pas @@ -55,7 +55,8 @@ uses USongs, USong, UMusic, - UThemes; + UThemes, + UPath; type TMidiNote = record @@ -128,7 +129,7 @@ type end; var - ConversionFileName: string; + ConversionFileName: IPath; implementation @@ -220,7 +221,7 @@ begin if CountSelectedTracks > 0 then begin Extract; - SResult := SaveSong(Song, Lines, ChangeFileExt(ConversionFileName, '.txt'), + SResult := SaveSong(Song, Lines, ConversionFileName.SetExtension('.txt'), false); if (SResult <> ssrOK) then begin @@ -499,7 +500,7 @@ begin AddButton(500, 20, 100, 40, Skin.GetTextureFileName('ButtonF')); AddButtonText(20, 5, 0, 0, 0, 'Save'); - ConversionFileName := 'D:/daten/africa.mid';//GamePath + 'file.mid'; + ConversionFileName := GamePath.Append('file.mid'); for P := 0 to 100 do begin @@ -525,7 +526,7 @@ begin MidiOut.Open; MidiFile := nil; - if FileExists(ConversionFileName) then + if ConversionFileName.Exists then begin MidiFile := TMidiFile.Create(nil); MidiFile.Filename := ConversionFileName; diff --git a/unicode/src/screens/UScreenEditHeader.pas b/unicode/src/screens/UScreenEditHeader.pas index d57f69f7..0ca98ca6 100644 --- a/unicode/src/screens/UScreenEditHeader.pas +++ b/unicode/src/screens/UScreenEditHeader.pas @@ -38,6 +38,7 @@ uses SDL, USongs, USong, + UPath, UThemes; type @@ -224,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); diff --git a/unicode/src/screens/UScreenEditSub.pas b/unicode/src/screens/UScreenEditSub.pas index 6bf9ea51..1d0649b4 100644 --- a/unicode/src/screens/UScreenEditSub.pas +++ b/unicode/src/screens/UScreenEditSub.pas @@ -132,7 +132,8 @@ uses USkins, ULanguage, UTextEncoding, - UUnicodeUtils; + UUnicodeUtils, + UPath; procedure OnSaveEncodingError(Value: boolean; Data: Pointer); @@ -142,7 +143,7 @@ begin if (Value) then begin CurrentSong.Encoding := encUTF8; - SResult := SaveSong(CurrentSong, Lines[0], CurrentSong.Path + CurrentSong.FileName, + SResult := SaveSong(CurrentSong, Lines[0], CurrentSong.Path.Append(CurrentSong.FileName), boolean(Data)); end; end; @@ -179,7 +180,7 @@ begin Ord('S'): begin // Save Song - SResult := SaveSong(CurrentSong, Lines[0], CurrentSong.Path + CurrentSong.FileName, + SResult := SaveSong(CurrentSong, Lines[0], CurrentSong.Path.Append(CurrentSong.FileName), (SDL_ModState = KMOD_LSHIFT)); if (SResult = ssrEncodingError) then begin @@ -1258,6 +1259,8 @@ begin end; procedure TScreenEditSub.onShow; +var + FileExt: IPath; begin inherited; @@ -1267,11 +1270,12 @@ begin 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 + Error := not CurrentSong.LoadSong(); except Error := true; end; @@ -1293,12 +1297,12 @@ 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); + AudioPlayback.Open(CurrentSong.Path.Append(CurrentSong.Mp3)); //Set Down Music Volume for Better hearability of Midi Sounds //Music.SetVolume(0.4); diff --git a/unicode/src/screens/UScreenOpen.pas b/unicode/src/screens/UScreenOpen.pas index a25356ec..08024a22 100644 --- a/unicode/src/screens/UScreenOpen.pas +++ b/unicode/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,8 +49,6 @@ uses UTexture, UMenuText, ULyrics, - Math, - gl, UThemes; type @@ -55,10 +56,10 @@ type private TextF: array[0..1] of integer; TextN: integer; + fPath: IPath; public Tex_Background: TTexture; FadeOut: boolean; - Path: string; BackScreen: pointer; procedure AddBox(X, Y, W, H: real); constructor Create; override; @@ -107,7 +108,7 @@ begin SDLK_ESCAPE: begin //Empty Filename and go to last Screen - ConversionFileName := ''; + ConversionFileName := PATH_NONE; AudioPlayback.PlaySound(SoundLib.Back); FadeTo(BackScreen); end; @@ -117,14 +118,14 @@ begin if (Interaction = 2) then begin //Update Filename and go to last Screen - ConversionFileName := Text[TextN].Text; + ConversionFileName := Path(Text[TextN].Text); AudioPlayback.PlaySound(SoundLib.Back); FadeTo(BackScreen); end else if (Interaction = 1) then begin //Empty Filename and go to last Screen - ConversionFileName := ''; + ConversionFileName := PATH_NONE; AudioPlayback.PlaySound(SoundLib.Back); FadeTo(BackScreen); end; @@ -176,7 +177,7 @@ begin // file name AddBox(20, 540, 500, 40); - TextN := AddText(50, 548, 0, 24, 0, 0, 0, ConversionFileName); + TextN := AddText(50, 548, 0, 24, 0, 0, 0, ConversionFileName.ToUTF8); AddInteraction(iText, TextN); // buttons diff --git a/unicode/src/screens/UScreenOptionsRecord.pas b/unicode/src/screens/UScreenOptionsRecord.pas index cdacd5f6..d1b62e2b 100644 --- a/unicode/src/screens/UScreenOptionsRecord.pas +++ b/unicode/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; diff --git a/unicode/src/screens/UScreenOptionsThemes.pas b/unicode/src/screens/UScreenOptionsThemes.pas index 163d8af9..e40ba664 100644 --- a/unicode/src/screens/UScreenOptionsThemes.pas +++ b/unicode/src/screens/UScreenOptionsThemes.pas @@ -136,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(); @@ -188,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; diff --git a/unicode/src/screens/UScreenPartyOptions.pas b/unicode/src/screens/UScreenPartyOptions.pas index b7226a5e..007956d6 100644 --- a/unicode/src/screens/UScreenPartyOptions.pas +++ b/unicode/src/screens/UScreenPartyOptions.pas @@ -68,13 +68,13 @@ type 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 @@ -240,14 +240,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; diff --git a/unicode/src/screens/UScreenPartyScore.pas b/unicode/src/screens/UScreenPartyScore.pas index 6b6d98e6..9e090715 100644 --- a/unicode/src/screens/UScreenPartyScore.pas +++ b/unicode/src/screens/UScreenPartyScore.pas @@ -166,7 +166,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 @@ -177,7 +179,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 @@ -188,7 +192,9 @@ 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; diff --git a/unicode/src/screens/UScreenPopup.pas b/unicode/src/screens/UScreenPopup.pas index 906335e7..2f6d981d 100644 --- a/unicode/src/screens/UScreenPopup.pas +++ b/unicode/src/screens/UScreenPopup.pas @@ -55,7 +55,7 @@ type constructor Create; override; function ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; override; procedure OnShow; override; - procedure ShowPopup(Msg: string; Handler: TPopupCheckHandler; + procedure ShowPopup(const Msg: UTF8String; Handler: TPopupCheckHandler; HandlerData: Pointer; DefaultValue: boolean = false); function Draw: boolean; override; end; @@ -73,7 +73,7 @@ type function ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; override; procedure OnShow; override; procedure OnHide; override; - procedure ShowPopup(msg: string); + procedure ShowPopup(const Msg: UTF8String); function Draw: boolean; override; end; @@ -165,7 +165,7 @@ begin inherited; end; -procedure TScreenPopupCheck.ShowPopup(Msg: string; Handler: TPopupCheckHandler; +procedure TScreenPopupCheck.ShowPopup(const Msg: UTF8String; Handler: TPopupCheckHandler; HandlerData: Pointer; DefaultValue: boolean); begin if (DefaultValue) then @@ -253,7 +253,7 @@ procedure TScreenPopupError.onHide; begin end; -procedure TScreenPopupError.ShowPopup(msg: string); +procedure TScreenPopupError.ShowPopup(const Msg: UTF8String); begin Interaction := 0; //Reset Interaction Visible := true; //Set Visible diff --git a/unicode/src/screens/UScreenSing.pas b/unicode/src/screens/UScreenSing.pas index dc3d5f5f..b8d0d9b7 100644 --- a/unicode/src/screens/UScreenSing.pas +++ b/unicode/src/screens/UScreenSing.pas @@ -49,6 +49,7 @@ uses USongs, UTexture, UThemes, + UPath, UTime; type @@ -217,6 +218,8 @@ end; // pause mod procedure TScreenSing.Pause; +var + VideoFile: IPath; begin if (not Paused) then // enable pause begin @@ -229,8 +232,8 @@ begin AudioPlayback.Pause; // pause video - if (CurrentSong.Video <> '') and FileExists(CurrentSong.Path + - CurrentSong.Video) then + VideoFile := CurrentSong.Path.Append(CurrentSong.Video); + if (CurrentSong.Video.IsSet) and VideoFile.Exists then fCurrentVideoPlaybackEngine.Pause; end @@ -242,8 +245,8 @@ begin AudioPlayback.Play; // video - if (CurrentSong.Video <> '') and FileExists(CurrentSong.Path + - CurrentSong.Video) then + VideoFile := CurrentSong.Path.Append(CurrentSong.Video); + if (CurrentSong.Video.IsSet) and VideoFile.Exists then fCurrentVideoPlaybackEngine.Pause; Paused := false; @@ -318,7 +321,7 @@ var V2M: boolean; V3R: boolean; Color: TRGB; - + VideoFile, BgFile: IPath; success: boolean; begin inherited; @@ -424,7 +427,7 @@ begin // 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 + if CurrentSong.FileName.GetExtension.ToUTF8 = '.xml' then success := CurrentSong.LoadXMLSong() else success := CurrentSong.LoadSong(); @@ -468,9 +471,10 @@ begin *} VideoLoaded := false; fShowVisualization := false; - if (CurrentSong.Video <> '') and FileExists(CurrentSong.Path + CurrentSong.Video) then + 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; fCurrentVideoPlaybackEngine := VideoPlayback; @@ -483,15 +487,17 @@ begin {* * 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; diff --git a/unicode/src/screens/UScreenSingModi.pas b/unicode/src/screens/UScreenSingModi.pas index d361943e..ad116a62 100644 --- a/unicode/src/screens/UScreenSingModi.pas +++ b/unicode/src/screens/UScreenSingModi.pas @@ -47,7 +47,7 @@ uses ULyrics, TextGL, gl, - + UPath, UThemes, UScreenSing, ModiSDK; @@ -71,7 +71,7 @@ type type TCustomSoundEntry = record - Filename : string; + Filename : IPath; Stream : TAudioPlaybackStream; end; @@ -503,19 +503,20 @@ end; 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)); //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; @@ -544,20 +545,21 @@ function LoadSound(const Name: PChar): cardinal; var Stream: TAudioPlaybackStream; i: integer; - Filename: string; + 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; diff --git a/unicode/src/screens/UScreenSong.pas b/unicode/src/screens/UScreenSong.pas index 00d90930..60938887 100644 --- a/unicode/src/screens/UScreenSong.pas +++ b/unicode/src/screens/UScreenSong.pas @@ -38,6 +38,7 @@ uses SDL, UCommon, UDisplay, + UPath, UFiles, UIni, ULanguage, @@ -842,9 +843,9 @@ var I: integer; CoverButtonIndex: integer; CoverButton: TButton; - CoverName: string; CoverTexture: TTexture; Cover: TCover; + CoverFile: IPath; Song: TSong; begin if (Length(CatSongs.Song) <= 0) then @@ -859,7 +860,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 @@ -868,18 +869,16 @@ 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 := ''; - - if (Song.Cover = '') then - CoverName := Skin.GetTextureFileName('SongCover') - else - CoverName := Song.Path + Song.Cover; + CoverFile := Song.Path.Append(Song.Cover); + if (not CoverFile.IsFile()) then + Song.Cover := PATH_NONE; + 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. @@ -919,7 +918,7 @@ 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; @@ -1472,7 +1471,7 @@ begin // if preview is deactivated: load musicfile now if (IPreviewVolumeVals[Ini.PreviewVolume] = 0) then - AudioPlayback.Open(CatSongs.Song[Interaction].Path + CatSongs.Song[Interaction].Mp3); + AudioPlayback.Open(CatSongs.Song[Interaction].Path.Append(CatSongs.Song[Interaction].Mp3)); // if hide then stop music (for party mode popup on exit) if (Display.NextScreen <> @ScreenSing) and @@ -1651,7 +1650,7 @@ 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 AudioPlayback.Position := AudioPlayback.Length / 4; // set preview volume diff --git a/unicode/src/screens/UScreenSongMenu.pas b/unicode/src/screens/UScreenSongMenu.pas index e09dd9b5..788611a1 100644 --- a/unicode/src/screens/UScreenSongMenu.pas +++ b/unicode/src/screens/UScreenSongMenu.pas @@ -73,7 +73,7 @@ const SM_Party_Joker = 128 or 2; var - ISelections: array of string; + ISelections: array of UTF8String; SelectValue: integer; implementation diff --git a/unicode/src/screens/UScreenStatMain.pas b/unicode/src/screens/UScreenStatMain.pas index 6d4d3f5b..17810cd6 100644 --- a/unicode/src/screens/UScreenStatMain.pas +++ b/unicode/src/screens/UScreenStatMain.pas @@ -166,7 +166,7 @@ begin //Set Songs with Vid SongsWithVid := 0; for I := 0 to Songs.SongList.Count -1 do - if (TSong(Songs.SongList[I]).Video <> '') then + if (TSong(Songs.SongList[I]).Video.IsSet) then Inc(SongsWithVid); end; diff --git a/unicode/src/ultrastardx.dpr b/unicode/src/ultrastardx.dpr index ea701a37..0ac212a1 100644 --- a/unicode/src/ultrastardx.dpr +++ b/unicode/src/ultrastardx.dpr @@ -210,8 +210,8 @@ uses UFont in 'base\UFont.pas', UTextEncoding in 'base\UTextEncoding.pas', - //UFilename in 'base\UFilename.pas', - //UFilesystem in 'base\UFilesystem.pas', + UPath in 'base\UPath.pas', + UFilesystem in 'base\UFilesystem.pas', //------------------------------ //Includes - Plugin Support -- cgit v1.2.3