{* 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.