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