From 446eec893b7915d80a4504d40bbfc6f77cafa550 Mon Sep 17 00:00:00 2001
From: tobigun <tobigun@b956fd51-792f-4845-bead-9b4dfca2ff2c>
Date: Thu, 23 Jul 2009 15:31:30 +0000
Subject: - IPath added - UFilesystem/UUnicodeUtils update

git-svn-id: svn://svn.code.sf.net/p/ultrastardx/svn/branches/experimental@1874 b956fd51-792f-4845-bead-9b4dfca2ff2c
---
 unicode/src/base/UFilesystem.pas   | 377 +++++++++++++----
 unicode/src/base/UPath.pas         | 847 +++++++++++++++++++++++++++++++++++++
 unicode/src/base/UUnicodeUtils.pas |  70 ++-
 3 files changed, 1194 insertions(+), 100 deletions(-)
 create mode 100644 unicode/src/base/UPath.pas

(limited to 'unicode/src')

diff --git a/unicode/src/base/UFilesystem.pas b/unicode/src/base/UFilesystem.pas
index e654f198..480d3376 100644
--- a/unicode/src/base/UFilesystem.pas
+++ b/unicode/src/base/UFilesystem.pas
@@ -34,57 +34,102 @@ interface
 {$I switches.inc}
 
 uses
+  SysUtils,
+  Classes,
   {$IFDEF MSWINDOWS}
   Windows,
   TntSysUtils,
   {$ENDIF}
-  UFilename,
-  SysUtils,
-  Classes;
+  UPath;
 
 type
-  {$IFDEF MSWINDOWSe}
+  {$IFDEF MSWINDOWS}
   TSytemSearchRec = TSearchRecW;
   {$ELSE}
   TSytemSearchRec = TSearchRec;
   {$ENDIF}
 
-  TUniSearchRec = record
-    Time: Integer;
-    Size: Int64;
-    Attr: Integer;
-    Name: IPath;
-    ExcludeAttr: Integer;
-    //FindHandle(): THandle;
+  TFileInfo = record
+    Time: Integer;  // timestamp
+    Size: Int64;    // file size (byte)
+    Attr: Integer;  // file attributes
+    Name: IPath;    // basename with extension
   end;
 
-  IFileFindIterator = interface
+  {**
+   * Iterates through the search results retrieved by FileFind().
+   * Example usage:
+   *   while(Iter.HasNext()) do
+   *     SearchRec := Iter.Next();
+   *}
+  IFileIterator = interface
     function HasNext(): Boolean;
-    function Next(): TUniSearchRec;
+    function Next(): TFileInfo;
   end;
 
+  {**
+   * Wrapper for SysUtils file functions.
+   * For documentation and examples, check the SysUtils equivalent.
+   *}
   IFileSystem = interface
     function ExpandFileName(const FileName: IPath): IPath;
-    function FileCreate(const FileName: IPath): Integer;
+    function FileCreate(const FileName: IPath): THandle;
     function DirectoryCreate(const Dir: IPath): Boolean;
-    function FileOpen(const FileName: IPath; Mode: LongWord): Integer;
+    function FileOpen(const FileName: IPath; Mode: LongWord): THandle;
     function FileAge(const FileName: IPath): Integer; overload;
     function FileAge(const FileName: IPath; out FileDateTime: TDateTime): Boolean; overload;
+
     function DirectoryExists(const Name: IPath): Boolean;
+
+    {**
+     * On Windows: returns true only for files (not directories)
+     * On Apple/Unix: returns true for all kind of files (even directories)
+     * @seealso SysUtils.FileExists()
+     *}
     function FileExists(const Name: IPath): Boolean;
+
     function FileGetAttr(const FileName: IPath): Cardinal;
     function FileSetAttr(const FileName: IPath; Attr: Integer): Boolean;
     function FileIsReadOnly(const FileName: IPath): Boolean;
     function FileSetReadOnly(const FileName: IPath; ReadOnly: Boolean): Boolean;
+    function FileIsAbsolute(const FileName: IPath): Boolean;
     function ForceDirectories(const Dir: IPath): Boolean;
-    function FileSearch(const Name: IPath; DirList: array of IPath): IPath;
     function RenameFile(const OldName, NewName: IPath): Boolean;
     function DeleteFile(const FileName: IPath): Boolean;
     function RemoveDir(const Dir: IPath): Boolean;
+
+    {**
+     * Copies file Source to Target. If FailIfExists is true, the file is not
+     * copied if it already exists.
+     * Returns true if the file was successfully copied.
+     *}
     function CopyFile(const Source, Target: IPath; FailIfExists: Boolean): Boolean;
 
-    function FileFind(const FilePattern: IPath; Attr: Integer): IFileFindIterator;
+    function ExtractFileDrive(const FileName: IPath): IPath;
+    function ExtractFilePath(const FileName: IPath): IPath;
+    function ExtractFileDir(const FileName: IPath): IPath;
+    function ExtractFileName(const FileName: IPath): IPath;
+    function ExtractFileExt(const FileName: IPath): IPath;
+    function ExtractRelativePath(const BaseName: IPath; const FileName: IPath): IPath;
+
+    function ChangeFileExt(const FileName: IPath; const Extension: IPath): IPath;
+
+    function IncludeTrailingPathDelimiter(const FileName: IPath): IPath;
+    function ExcludeTrailingPathDelimiter(const FileName: IPath): IPath;
+
+    {**
+     * Searches for a file with filename Name in the directories given in DirList.
+     *}
+    function FileSearch(const Name: IPath; DirList: array of IPath): IPath;
+
+    {**
+     * More convenient version of FindFirst/Next/Close with iterator support.
+     *}
+    function FileFind(const FilePattern: IPath; Attr: Integer): IFileIterator;
 
+    {**
+     * Old style search functions. Use FileFind() instead.
+     *}
     function FindFirst(const FilePattern: IPath; Attr: Integer; var F: TSytemSearchRec): Integer;
     function FindNext(var F: TSytemSearchRec): Integer;
     procedure FindClose(var F: TSytemSearchRec);
@@ -92,11 +137,19 @@ type
     function GetCurrentDir: IPath;
     function SetCurrentDir(const Dir: IPath): Boolean;
 
+    {**
+     * Opens a file stream for FileName with Mode. Close the stream after usage
+     * with THandleStream.Free().
+     *}
     function CreateFileStream(const FileName: IPath; Mode: Word): THandleStream;
+
+    {**
+     * Returns true if the filesystem is case-sensitive.
+     *}
+    function IsCaseSensitive(): boolean;
   end;
 
-var
-  FileSystem: IFileSystem;
+  function FileSystem(): IFileSystem;
 
 implementation
 
@@ -104,9 +157,9 @@ type
   TFileSystemImpl = class(TInterfacedObject, IFileSystem)
   public
     function ExpandFileName(const FileName: IPath): IPath;
-    function FileCreate(const FileName: IPath): Integer;
+    function FileCreate(const FileName: IPath): THandle;
     function DirectoryCreate(const Dir: IPath): Boolean;
-    function FileOpen(const FileName: IPath; Mode: LongWord): Integer;
+    function FileOpen(const FileName: IPath; Mode: LongWord): THandle;
     function FileAge(const FileName: IPath): Integer; overload;
     function FileAge(const FileName: IPath; out FileDateTime: TDateTime): Boolean; overload;
     function DirectoryExists(const Name: IPath): Boolean;
@@ -115,14 +168,25 @@ type
     function FileSetAttr(const FileName: IPath; Attr: Integer): Boolean;
     function FileIsReadOnly(const FileName: IPath): Boolean;
     function FileSetReadOnly(const FileName: IPath; ReadOnly: Boolean): Boolean;
+    function FileIsAbsolute(const FileName: IPath): Boolean;
     function ForceDirectories(const Dir: IPath): Boolean;
-    function FileSearch(const Name: IPath; DirList: array of IPath): IPath;
     function RenameFile(const OldName, NewName: IPath): Boolean;
     function DeleteFile(const FileName: IPath): Boolean;
     function RemoveDir(const Dir: IPath): Boolean;
     function CopyFile(const Source, Target: IPath; FailIfExists: Boolean): Boolean;
 
-    function FileFind(const FilePattern: IPath; Attr: Integer): IFileFindIterator;
+    function ExtractFileDrive(const FileName: IPath): IPath;
+    function ExtractFilePath(const FileName: IPath): IPath;
+    function ExtractFileDir(const FileName: IPath): IPath;
+    function ExtractFileName(const FileName: IPath): IPath;
+    function ExtractFileExt(const FileName: IPath): IPath;
+    function ExtractRelativePath(const BaseName: IPath; const FileName: IPath): IPath;
+    function ChangeFileExt(const FileName: IPath; const Extension: IPath): IPath;
+    function IncludeTrailingPathDelimiter(const FileName: IPath): IPath;
+    function ExcludeTrailingPathDelimiter(const FileName: IPath): IPath;
+
+    function FileSearch(const Name: IPath; DirList: array of IPath): IPath;
+    function FileFind(const FilePattern: IPath; Attr: Integer): IFileIterator;
 
     function FindFirst(const FilePattern: IPath; Attr: Integer; var F: TSytemSearchRec): Integer;
     function FindNext(var F: TSytemSearchRec): Integer;
@@ -132,9 +196,11 @@ type
     function SetCurrentDir(const Dir: IPath): Boolean;
 
     function CreateFileStream(const FileName: IPath; Mode: Word): THandleStream;
+
+    function IsCaseSensitive(): boolean;
   end;
 
-  TFileFindIterator = class(TInterfacedObject, IFileFindIterator)
+  TFileIterator = class(TInterfacedObject, IFileIterator)
   private
     fHasNext: Boolean;
     fSearchRec: TSytemSearchRec;
@@ -143,85 +209,127 @@ type
     destructor Destroy(); override;
 
     function HasNext(): Boolean;
-    function Next(): TUniSearchRec;
+    function Next(): TFileInfo;
   end;
 
 
+var
+  FileSystem_Singleton: IFileSystem;
+
+function FileSystem(): IFileSystem;
+begin
+  Result := FileSystem_Singleton;
+end;
+
 function TFileSystemImpl.CreateFileStream(const FileName: IPath; Mode: Word): THandleStream;
 begin
-  Result := TUniFileStream.Create(FileName, Mode);
+  Result := TUnicodeFileStream.Create(FileName, Mode);
 end;
 
-function TFileSystemImpl.FileFind(const FilePattern: IPath; Attr: Integer): IFileFindIterator;
+function TFileSystemImpl.FileFind(const FilePattern: IPath; Attr: Integer): IFileIterator;
 begin
-  Result := TFileFindIterator.Create(FilePattern, Attr);
+  Result := TFileIterator.Create(FilePattern, Attr);
 end;
 
-{$IFDEF MSWINDOWSs}
+function TFileSystemImpl.IsCaseSensitive(): boolean;
+begin
+  // Windows and Mac OS X do not have case sensitive file systems
+  {$IF Defined(MSWINDOWS) or Defined(DARWIN)}
+    Result := false;
+  {$ELSE}
+    Result := true;
+  {$IFEND}
+end;
+
+function TFileSystemImpl.FileIsAbsolute(const FileName: IPath): Boolean;
+var
+  NameStr: UTF8String;
+begin
+  Result := true;
+  NameStr := FileName.ToUTF8();
+
+  {$IFDEF MSWINDOWS}
+    // check if drive is given 'C:...'
+    if (FileName.GetDrive().ToUTF8 <> '') then
+      Exit;
+    // check if path starts with '\\'
+    if (Length(NameStr) >= 2) and
+       (NameStr[1] = PathDelim) and (NameStr[2] = PathDelim) then
+      Exit;
+  {$ELSE} // Unix based systems
+    // check if root dir given '/...'
+    if (Length(NameStr) >= 1) and (NameStr[1] = PathDelim) then
+      Exit;
+  {$ENDIF}
+
+  Result := false;
+end;
+
+{$IFDEF MSWINDOWS}
 
 function TFileSystemImpl.ExpandFileName(const FileName: IPath): IPath;
 begin
-  Result := Path(WideExpandFileName(FileName.ToWideString()));
+  Result := Path(WideExpandFileName(FileName.ToWide()));
 end;
 
-function TFileSystemImpl.FileCreate(const FileName: IPath): Integer;
+function TFileSystemImpl.FileCreate(const FileName: IPath): THandle;
 begin
-  Result := WideFileCreate(FileName.ToWideString());
+  Result := WideFileCreate(FileName.ToWide());
 end;
 
 function TFileSystemImpl.DirectoryCreate(const Dir: IPath): Boolean;
 begin
-  Result := WideCreateDir(Dir.ToWideString());
+  Result := WideCreateDir(Dir.ToWide());
 end;
 
-function TFileSystemImpl.FileOpen(const FileName: IPath; Mode: LongWord): Integer;
+function TFileSystemImpl.FileOpen(const FileName: IPath; Mode: LongWord): THandle;
 begin
-  Result := WideFileOpen(FileName.ToWideString(), Mode);
+  Result := WideFileOpen(FileName.ToWide(), Mode);
 end;
 
 function TFileSystemImpl.FileAge(const FileName: IPath): Integer;
 begin
-  Result := WideFileAge(FileName.ToWideString());
+  Result := WideFileAge(FileName.ToWide());
 end;
 
 function TFileSystemImpl.FileAge(const FileName: IPath; out FileDateTime: TDateTime): Boolean;
 begin
-  Result := WideFileAge(FileName.ToWideString(), FileDateTime);
+  Result := WideFileAge(FileName.ToWide(), FileDateTime);
 end;
 
 function TFileSystemImpl.DirectoryExists(const Name: IPath): Boolean;
 begin
-  Result := WideDirectoryExists(Name.ToWideString());
+  Result := WideDirectoryExists(Name.ToWide());
 end;
 
 function TFileSystemImpl.FileExists(const Name: IPath): Boolean;
 begin
-  Result := WideFileExists(Name.ToWideString());
+  Result := WideFileExists(Name.ToWide());
 end;
 
 function TFileSystemImpl.FileGetAttr(const FileName: IPath): Cardinal;
 begin
-  Result := WideFileGetAttr(FileName.ToWideString());
+  Result := WideFileGetAttr(FileName.ToWide());
 end;
 
 function TFileSystemImpl.FileSetAttr(const FileName: IPath; Attr: Integer): Boolean;
 begin
-  Result := WideFileSetAttr(FileName.ToWideString(), Attr);
+  Result := WideFileSetAttr(FileName.ToWide(), Attr);
 end;
 
 function TFileSystemImpl.FileIsReadOnly(const FileName: IPath): Boolean;
 begin
-  Result := WideFileIsReadOnly(FileName.ToWideString());
+  Result := WideFileIsReadOnly(FileName.ToWide());
 end;
 
 function TFileSystemImpl.FileSetReadOnly(const FileName: IPath; ReadOnly: Boolean): Boolean;
 begin
-  Result := WideFileSetReadOnly(FileName.ToWideString(), ReadOnly);
+  Result := WideFileSetReadOnly(FileName.ToWide(), ReadOnly);
 end;
 
 function TFileSystemImpl.ForceDirectories(const Dir: IPath): Boolean;
 begin
-  Result := WideForceDirectories(Dir.ToWideString());
+  Result := WideForceDirectories(Dir.ToWide());
 end;
 
 function TFileSystemImpl.FileSearch(const Name: IPath; DirList: array of IPath): IPath;
@@ -234,34 +342,79 @@ begin
   begin
     if (I > 0) then
       DirListStr := DirListStr + PathSep;
-    DirListStr := DirListStr + DirList[I].ToWideString();
+    DirListStr := DirListStr + DirList[I].ToWide();
   end;
-  Result := Path(WideFileSearch(Name.ToWideString(), DirListStr));
+  Result := Path(WideFileSearch(Name.ToWide(), DirListStr));
 end;
 
 function TFileSystemImpl.RenameFile(const OldName, NewName: IPath): Boolean;
 begin
-  Result := WideRenameFile(OldName.ToWideString(), NewName.ToWideString());
+  Result := WideRenameFile(OldName.ToWide(), NewName.ToWide());
 end;
 
 function TFileSystemImpl.DeleteFile(const FileName: IPath): Boolean;
 begin
-  Result := WideDeleteFile(FileName.ToWideString());
+  Result := WideDeleteFile(FileName.ToWide());
 end;
 
 function TFileSystemImpl.RemoveDir(const Dir: IPath): Boolean;
 begin
-  Result := WideRemoveDir(Dir.ToWideString());
+  Result := WideRemoveDir(Dir.ToWide());
 end;
 
 function TFileSystemImpl.CopyFile(const Source, Target: IPath; FailIfExists: Boolean): Boolean;
 begin
-  Result := WideCopyFile(Source.ToWideString(), Target.ToWideString(), FailIfExists);
+  Result := WideCopyFile(Source.ToWide(), Target.ToWide(), FailIfExists);
+end;
+
+function TFileSystemImpl.ExtractFileDrive(const FileName: IPath): IPath;
+begin
+  Result := Path(WideExtractFileDrive(FileName.ToWide()));
+end;
+
+function TFileSystemImpl.ExtractFilePath(const FileName: IPath): IPath;
+begin
+  Result := Path(WideExtractFilePath(FileName.ToWide()));
+end;
+
+function TFileSystemImpl.ExtractFileDir(const FileName: IPath): IPath;
+begin
+  Result := Path(WideExtractFileDir(FileName.ToWide()));
+end;
+
+function TFileSystemImpl.ExtractFileName(const FileName: IPath): IPath;
+begin
+  Result := Path(WideExtractFileName(FileName.ToWide()));
+end;
+
+function TFileSystemImpl.ExtractFileExt(const FileName: IPath): IPath;
+begin
+  Result := Path(WideExtractFileExt(FileName.ToWide()));
+end;
+
+function TFileSystemImpl.ExtractRelativePath(const BaseName: IPath; const FileName: IPath): IPath;
+begin
+  Result := Path(WideExtractRelativePath(BaseName.ToWide(), FileName.ToWide()));
+end;
+
+function TFileSystemImpl.ChangeFileExt(const FileName: IPath; const Extension: IPath): IPath;
+begin
+  Result := Path(WideChangeFileExt(FileName.ToWide(), Extension.ToWide()));
+end;
+
+function TFileSystemImpl.IncludeTrailingPathDelimiter(const FileName: IPath): IPath;
+begin
+  Result := Path(WideIncludeTrailingPathDelimiter(FileName.ToWide()));
+end;
+
+function TFileSystemImpl.ExcludeTrailingPathDelimiter(const FileName: IPath): IPath;
+begin
+  Result := Path(WideExcludeTrailingPathDelimiter(FileName.ToWide()));
 end;
 
 function TFileSystemImpl.FindFirst(const FilePattern: IPath; Attr: Integer; var F: TSytemSearchRec): Integer;
 begin
-  Result := WideFindFirst(FilePattern.ToWideString(), Attr, F);
+  Result := WideFindFirst(FilePattern.ToWide(), Attr, F);
 end;
 
 function TFileSystemImpl.FindNext(var F: TSytemSearchRec): Integer;
@@ -281,79 +434,79 @@ end;
 
 function TFileSystemImpl.SetCurrentDir(const Dir: IPath): Boolean;
 begin
-  Result := WideSetCurrentDir(Dir.ToWideString());
+  Result := WideSetCurrentDir(Dir.ToWide());
 end;
 
 {$ELSE} // UNIX
 
 function TFileSystemImpl.ExpandFileName(const FileName: IPath): IPath;
 begin
-  Result := Path(SysUtils.ExpandFileName(FileName.ToString(pencSystemANSI)), pencSystemANSI);
+  Result := Path(SysUtils.ExpandFileName(FileName.ToNative()));
 end;
 
-function TFileSystemImpl.FileCreate(const FileName: IPath): Integer;
+function TFileSystemImpl.FileCreate(const FileName: IPath): THandle;
 begin
-  Result := SysUtils.FileCreate(FileName.ToString(pencSystemANSI));
+  Result := SysUtils.FileCreate(FileName.ToNative());
 end;
 
 function TFileSystemImpl.DirectoryCreate(const Dir: IPath): Boolean;
 begin
-  Result := SysUtils.CreateDir(Dir.ToString(pencSystemANSI));
+  Result := SysUtils.CreateDir(Dir.ToNative());
 end;
 
-function TFileSystemImpl.FileOpen(const FileName: IPath; Mode: LongWord): Integer;
+function TFileSystemImpl.FileOpen(const FileName: IPath; Mode: LongWord): THandle;
 begin
-  Result := SysUtils.FileOpen(FileName.ToString(pencSystemANSI), Mode);
+  Result := SysUtils.FileOpen(FileName.ToNative(), Mode);
 end;
 
 function TFileSystemImpl.FileAge(const FileName: IPath): Integer;
 begin
-  Result := SysUtils.FileAge(FileName.ToString(pencSystemANSI));
+  Result := SysUtils.FileAge(FileName.ToNative());
 end;
 
 function TFileSystemImpl.FileAge(const FileName: IPath; out FileDateTime: TDateTime): Boolean;
 var
-  FileDate: longint;
+  FileDate: Integer;
 begin
-  FileDate := SysUtils.FileAge(FileName.ToString(pencSystemANSI));
-  if (FileDate > -1) then
+  FileDate := SysUtils.FileAge(FileName.ToNative());
+  Result := (FileDate <> -1);
+  if (Result) then
     FileDateTime := FileDateToDateTime(FileDate);
-  Result := (FileDate > -1);
 end;
 
 function TFileSystemImpl.DirectoryExists(const Name: IPath): Boolean;
 begin
-  Result := SysUtils.DirectoryExists(Name.ToString(pencSystemANSI));
+  Result := SysUtils.DirectoryExists(Name.ToNative());
 end;
 
 function TFileSystemImpl.FileExists(const Name: IPath): Boolean;
 begin
-  Result := SysUtils.FileExists(Name.ToString(pencSystemANSI));
+  Result := SysUtils.FileExists(Name.ToNative());
 end;
 
 function TFileSystemImpl.FileGetAttr(const FileName: IPath): Cardinal;
 begin
-  Result := SysUtils.FileGetAttr(FileName.ToString(pencSystemANSI));
+  Result := SysUtils.FileGetAttr(FileName.ToNative());
 end;
 
 function TFileSystemImpl.FileSetAttr(const FileName: IPath; Attr: Integer): Boolean;
 begin
-  Result := (SysUtils.FileSetAttr(FileName.ToString(pencSystemANSI), Attr) = 0);
+  Result := (SysUtils.FileSetAttr(FileName.ToNative(), Attr) = 0);
 end;
 
 function TFileSystemImpl.FileIsReadOnly(const FileName: IPath): Boolean;
 begin
-  Result := SysUtils.FileIsReadOnly(FileName.ToString(pencSystemANSI));
+  Result := SysUtils.FileIsReadOnly(FileName.ToNative());
 end;
 
 function TFileSystemImpl.FileSetReadOnly(const FileName: IPath; ReadOnly: Boolean): Boolean;
 begin
-  Result := (SysUtils.FileSetAttr(FileName.ToString(pencSystemANSI), faReadOnly) = 0);
+  Result := (SysUtils.FileSetAttr(FileName.ToNative(), faReadOnly) = 0);
 end;
 
 function TFileSystemImpl.ForceDirectories(const Dir: IPath): Boolean;
 begin
-  Result := SysUtils.ForceDirectories(Dir.ToString(pencSystemANSI));
+  Result := SysUtils.ForceDirectories(Dir.ToNative());
 end;
 
 function TFileSystemImpl.FileSearch(const Name: IPath; DirList: array of IPath): IPath;
@@ -366,24 +519,24 @@ begin
   begin
     if (I > 0) then
       DirListStr := DirListStr + PathSep;
-    DirListStr := DirListStr + DirList[I].ToString(pencSystemANSI);
+    DirListStr := DirListStr + DirList[I].ToNative();
   end;
-  Result := Path(SysUtils.FileSearch(Name.ToString(pencSystemANSI), DirListStr), pencSystemANSI);
+  Result := Path(SysUtils.FileSearch(Name.ToNative(), DirListStr));
 end;
 
 function TFileSystemImpl.RenameFile(const OldName, NewName: IPath): Boolean;
 begin
-  Result := SysUtils.RenameFile(OldName.ToString(pencSystemANSI), NewName.ToString(pencSystemANSI));
+  Result := SysUtils.RenameFile(OldName.ToNative(), NewName.ToNative());
 end;
 
 function TFileSystemImpl.DeleteFile(const FileName: IPath): Boolean;
 begin
-  Result := SysUtils.DeleteFile(FileName.ToString(pencSystemANSI));
+  Result := SysUtils.DeleteFile(FileName.ToNative());
 end;
 
 function TFileSystemImpl.RemoveDir(const Dir: IPath): Boolean;
 begin
-  Result := SysUtils.RemoveDir(Dir.ToString(pencSystemANSI));
+  Result := SysUtils.RemoveDir(Dir.ToNative());
 end;
 
 function TFileSystemImpl.CopyFile(const Source, Target: IPath; FailIfExists: Boolean): Boolean;
@@ -405,8 +558,8 @@ begin
   try
     try
       // open source and target file (might throw an exception on error)
-      SourceFile := TFileStream.Create(Source.ToString(pencSystemANSI), fmOpenRead);
-      TargetFile := TFileStream.Create(Target.ToString(pencSystemANSI), fmCreate or fmOpenWrite);
+      SourceFile := TFileStream.Create(Source.ToNative(), fmOpenRead);
+      TargetFile := TFileStream.Create(Target.ToNative(), fmCreate or fmOpenWrite);
 
       while true do
       begin
@@ -429,9 +582,54 @@ begin
   Result := true;
 end;
 
+function TFileSystemImpl.ExtractFileDrive(const FileName: IPath): IPath;
+begin
+  Result := Path(SysUtils.ExtractFileDrive(FileName.ToNative()));
+end;
+
+function TFileSystemImpl.ExtractFilePath(const FileName: IPath): IPath;
+begin
+  Result := Path(SysUtils.ExtractFilePath(FileName.ToNative()));
+end;
+
+function TFileSystemImpl.ExtractFileDir(const FileName: IPath): IPath;
+begin
+  Result := Path(SysUtils.ExtractFileDir(FileName.ToNative()));
+end;
+
+function TFileSystemImpl.ExtractFileName(const FileName: IPath): IPath;
+begin
+  Result := Path(SysUtils.ExtractFileName(FileName.ToNative()));
+end;
+
+function TFileSystemImpl.ExtractFileExt(const FileName: IPath): IPath;
+begin
+  Result := Path(SysUtils.ExtractFileExt(FileName.ToNative()));
+end;
+
+function TFileSystemImpl.ExtractRelativePath(const BaseName: IPath; const FileName: IPath): IPath;
+begin
+  Result := Path(SysUtils.ExtractRelativePath(BaseName.ToNative(), FileName.ToNative()));
+end;
+
+function TFileSystemImpl.ChangeFileExt(const FileName: IPath; const Extension: IPath): IPath;
+begin
+  Result := Path(SysUtils.ChangeFileExt(FileName.ToNative(), Extension.ToNative()));
+end;
+
+function TFileSystemImpl.IncludeTrailingPathDelimiter(const FileName: IPath): IPath;
+begin
+  Result := Path(SysUtils.IncludeTrailingPathDelimiter(FileName.ToNative()));
+end;
+
+function TFileSystemImpl.ExcludeTrailingPathDelimiter(const FileName: IPath): IPath;
+begin
+  Result := Path(SysUtils.ExcludeTrailingPathDelimiter(FileName.ToNative()));
+end;
+
 function TFileSystemImpl.FindFirst(const FilePattern: IPath; Attr: Integer; var F: TSytemSearchRec): Integer;
 begin
-  Result := SysUtils.FindFirst(FilePattern.ToString(pencSystemANSI), Attr, F);
+  Result := SysUtils.FindFirst(FilePattern.ToNative(), Attr, F);
 end;
 
 function TFileSystemImpl.FindNext(var F: TSytemSearchRec): Integer;
@@ -446,37 +644,37 @@ end;
 
 function TFileSystemImpl.GetCurrentDir: IPath;
 begin
-  Result := Path(SysUtils.GetCurrentDir(), pencSystemANSI);
+  Result := Path(SysUtils.GetCurrentDir());
 end;
 
 function TFileSystemImpl.SetCurrentDir(const Dir: IPath): Boolean;
 begin
-  Result := SysUtils.SetCurrentDir(Dir.ToString(pencSystemANSI));
+  Result := SysUtils.SetCurrentDir(Dir.ToNative());
 end;
 
 {$ENDIF}
 
 
-{ TFileFindIterator }
+{ TFileIterator }
 
-constructor TFileFindIterator.Create(const FilePattern: IPath; Attr: Integer);
+constructor TFileIterator.Create(const FilePattern: IPath; Attr: Integer);
 begin
   inherited Create();
   fHasNext := (FileSystem.FindFirst(FilePattern, Attr, fSearchRec) = 0);
 end;
 
-destructor TFileFindIterator.Destroy();
+destructor TFileIterator.Destroy();
 begin
   FileSystem.FindClose(fSearchRec);
   inherited;
 end;
 
-function TFileFindIterator.HasNext(): Boolean;
+function TFileIterator.HasNext(): Boolean;
 begin
   Result := fHasNext;
 end;
 
-function TFileFindIterator.Next(): TUniSearchRec;
+function TFileIterator.Next(): TFileInfo;
 begin
   if (not fHasNext) then
   begin
@@ -487,12 +685,7 @@ begin
   Result.Time := fSearchRec.Time;
   Result.Size := fSearchRec.Size;
   Result.Attr := fSearchRec.Attr;
-  {$IFDEF MSWINDOWS}
   Result.Name := Path(fSearchRec.Name);
-  {$ELSE}
-  Result.Name := Path(fSearchRec.Name, pencSystemANSI);
-  {$ENDIF}
-  Result.ExcludeAttr := fSearchRec.ExcludeAttr;
 
   // fetch next entry
   fHasNext := (FileSystem.FindNext(fSearchRec) = 0);
@@ -500,9 +693,9 @@ end;
 
 
 initialization
-  FileSystem := TFileSystemImpl.Create;
+  FileSystem_Singleton := TFileSystemImpl.Create;
 
 finalization
-  FileSystem := nil;
+  FileSystem_Singleton := nil;
 
 end.
diff --git a/unicode/src/base/UPath.pas b/unicode/src/base/UPath.pas
new file mode 100644
index 00000000..5203e7eb
--- /dev/null
+++ b/unicode/src/base/UPath.pas
@@ -0,0 +1,847 @@
+{* UltraStar Deluxe - Karaoke Game
+ *
+ * UltraStar Deluxe is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *}
+
+unit UPath;
+
+{$IFDEF FPC}
+  {$MODE Delphi}
+{$ENDIF}
+
+{$I switches.inc}
+
+interface
+
+uses
+  SysUtils,
+  Classes,
+  {$IFDEF MSWINDOWS}
+  TntClasses,
+  {$ENDIF}
+  UUnicodeUtils;
+
+type
+  IPath = interface;
+
+  {**
+   * TUnicodeFileStream
+   *}
+  {$IFDEF MSWINDOWS}
+  TUnicodeFileStream = class(TTntFileStream)
+  {$ELSE}
+  TUnicodeFileStream = class(TFileStream)
+  {$ENDIF}
+  public
+    constructor Create(const FileName: IPath; Mode: Word);
+  end;
+
+  {**
+   * TUnicodeMemoryStream
+   *}
+  TUnicodeMemoryStream = class(TMemoryStream)
+  public
+    procedure LoadFromFile(const FileName: IPath);
+    procedure SaveToFile(const FileName: IPath);
+  end;
+
+  IPathDynArray = array of IPath;
+  
+  {**
+   * IPath
+   * The Path's pathname is immutable and cannot be changed after creation.
+   *}
+  IPath = interface
+    {**
+     * Returns the path as an UTF8 encoded string.
+     * If UseNativeDelim is set to true, the native path delimiter ('\' on win32)
+     * is used. If it is set to false the (more) portable '/' delimiter will used.
+     *}
+    function ToUTF8(UseNativeDelim: boolean = true): UTF8String;
+
+    {**
+     * Returns the path as an UTF-16 encoded string.
+     * If UseNativeDelim is set to true, the native path delimiter ('\' on win32)
+     * is used. If it is set to false the delimiter will be '/'.
+     *}
+    function ToWide(UseNativeDelim: boolean = true): WideString;
+
+    {**
+     * Returns the path with the system's native encoding and path delimiter.
+     * Win32: ANSI (use the UTF-16 version IPath.ToWide() whenever possible)
+     * Mac:   UTF8
+     * Unix:  UTF8 or ANSI according to LC_CTYPE
+     *}
+    function ToNative(): RawByteString;
+
+    {**
+     * Note: File must be closed with FileClose(Handle) after usage
+     * @seealso SysUtils.FileOpen()
+     *}
+    function Open(Mode: LongWord): THandle;
+
+    {**
+     * Note: Stream must be freed with TUniFileStream.Free after usage
+     *}
+    function OpenStream(Mode: Word): TUnicodeFileStream;
+
+    {** @seealso SysUtils.ExtractFileDrive() *}
+    function GetDrive(): IPath;
+
+    {** @seealso SysUtils.ExtractFilePath() *}
+    function GetPath(): IPath;
+
+    {** @seealso SysUtils.ExtractFileDir() *}
+    function GetDir(): IPath;
+
+    {** @seealso SysUtils.ExtractFileName() *}
+    function GetName(): IPath;
+
+    {** @seealso SysUtils.ExtractFileExtension() *}
+    function GetExtension(): IPath;
+
+    {**
+     * Returns a copy of the path with the extension changed to Extension.
+     * The file itself is not changed, use Rename() for this task.
+     * @seealso SysUtils.ChangeFileExt()
+     *}
+    function SetExtension(const Extension: IPath): IPath;
+
+    {**
+     * Returns the representation of the path relative to Basename.
+     * Note that the basename must be terminated with a path delimiter
+     * otherwise the last path component will be ignored.
+     * @seealso SysUtils.ExtractRelativePath()
+     *}
+    function GetRelativePath(const BaseName: IPath): IPath;
+
+    {** @seealso SysUtils.ExpandFileName() *}
+    function GetAbsolutePath(): IPath;
+
+    {**
+     * Returns the concatenation of this path with Child. If this path does not
+     * end with a path delimiter one is inserted in front of the Child path.
+     * Example: Path('parent').Append(Path('child')) -> Path('parent/child')
+     *}
+    function Append(const Child: IPath): IPath;
+
+    {**
+     * Splits the path into its components.
+     * Example: C:\test\dir -> ['C:\', 'test\', 'dir']
+     *}
+    function SplitDirs(): IPathDynArray;
+
+    {**
+     * Returns the parent directory or PathNone if none exists.
+     *}
+    function GetParent(): IPath;
+
+    {**
+     * Checks if this path is a subdir of or file inside Parent.
+     * If Direct is true this path must be a direct child.
+     * Example: C:\test\file is a direct child of C:\test and a child of C:\
+     *}
+    function IsChildOf(const Parent: IPath; Direct: boolean): boolean;
+
+    {**
+     * Adjusts the case of the path on case senstitive filesystems.
+     * If the path does not exist or the filesystem is case insensitive
+     * the original path will be returned. Otherwise a corrected copy.
+     *}
+    function AdjustCase(AdjustAllLevels: boolean): IPath;
+
+    {** @seealso SysUtils.IncludeTrailingPathDelimiter() *}
+    function IncludeTrailingPathDelimiter(): IPath;
+
+    {** @seealso SysUtils.ExcludeTrailingPathDelimiter() *}
+    function ExcludeTrailingPathDelimiter(): IPath;
+
+    function Exists(): boolean;
+    function IsFile(): boolean;
+    function IsDirectory(): boolean;
+    function IsAbsolute(): boolean;
+    function GetFileAge(): integer; overload;
+    function GetFileAge(out FileDateTime: TDateTime): boolean; overload;
+    function GetAttr(): cardinal;
+    function SetAttr(Attr: Integer): boolean;
+    function IsReadOnly(): boolean;
+    function SetReadOnly(ReadOnly: boolean): boolean;
+
+    {**
+     * 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;
+
+    {**
+     * Searches for a file in DirList. The Result is nil if the file was
+     * not found. Use IFileSystem.FileFind() instead if you want to use
+     * wildcards.
+     * @seealso SysUtils.FileSearch()
+     *}
+    function FileSearch(const DirList: IPath): IPath;
+
+    {** File must be closed with FileClose(Handle) after usage }
+    function CreateFile(): THandle;
+    function DeleteFile(): boolean;
+    function CreateDirectory(Force: boolean = false): boolean;
+    function DeleteEmptyDir(): boolean;
+    function Rename(const NewName: IPath): boolean;
+    function CopyFile(const Target: IPath; FailIfExists: boolean): boolean;
+
+    // TODO: Dirwatch stuff
+    // AddFileChangeListener(Listener: TFileChangeListener);
+  end;
+
+{**
+ * Creates a new path with the given pathname. PathName can be either in UTF8
+ * or the local encoding.
+ * Notes:
+ * - On Apple only UTF8 is supported
+ * - Same applies to Unix with LC_CTYPE set to UTF8 encoding (default on newer systems)
+ *}
+function Path(const PathName: RawByteString): IPath; overload;
+
+{**
+ * Creates a new path with the given UTF-16 pathname.
+ *}
+function Path(const PathName: WideString): IPath; overload;
+
+{**
+ * Returns a reference to a singleton with path simply set to ''.
+ *}
+function PathNone(): IPath;
+
+implementation
+
+uses
+  UFilesystem;
+
+type
+  TPathImpl = class(TInterfacedObject, IPath)
+    private
+      fName: UTF8String; //<** internal filename string, always UTF8 with PathDelim
+
+      {**
+       * Unifies the filename. Path-delimiters are replaced by '/'.
+       *}
+      procedure Unify();
+
+      {**
+       * Returns a copy of fName with path delimiters changed to '/'.
+       *}
+      function GetPortableString(): UTF8String;
+
+      procedure AssertRefCount; inline;
+
+    public
+      constructor Create(const Name: UTF8String);
+      destructor Destroy(); override;
+
+      function ToUTF8(UseNativeDelim: boolean): UTF8String;
+      function ToWide(UseNativeDelim: boolean): WideString;
+      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 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 IsChildOf(const Parent: IPath; Direct: boolean): boolean;
+
+      function AdjustCase(AdjustAllLevels: boolean): IPath;
+
+      function IncludeTrailingPathDelimiter(): IPath;
+      function ExcludeTrailingPathDelimiter(): IPath;
+
+      function GetFileAge(): integer; overload;
+      function GetFileAge(out FileDateTime: TDateTime): boolean; overload;
+      function Exists(): boolean;
+      function IsFile(): boolean;
+      function IsDirectory(): boolean;
+      function IsAbsolute(): boolean;
+      function GetAttr(): cardinal;
+      function SetAttr(Attr: Integer): boolean;
+      function IsReadOnly(): boolean;
+      function SetReadOnly(ReadOnly: boolean): boolean;
+      
+      function FileSearch(const DirList: IPath): IPath;
+
+      function CreateFile(): THandle;
+      function DeleteFile(): boolean;
+      function CreateDirectory(Force: boolean): boolean;
+      function DeleteEmptyDir(): boolean;
+      function Rename(const NewName: IPath): boolean;
+      function CopyFile(const Target: IPath; FailIfExists: boolean): boolean;
+  end;
+
+function Path(const PathName: RawByteString): IPath;
+begin
+  if (IsUTF8String(PathName)) then
+    Result := TPathImpl.Create(PathName)
+  else if (IsNativeUTF8()) then
+    Result := PathNone
+  else
+    Result := TPathImpl.Create(AnsiToUtf8(PathName));
+end;
+
+function Path(const PathName: WideString): IPath;
+begin
+  Result := TPathImpl.Create(UTF8Encode(PathName));
+end;
+
+
+
+procedure TPathImpl.AssertRefCount;
+begin
+  if (FRefCount <= 0) then
+    raise Exception.Create('RefCount error: ' + inttostr(FRefCount));
+end;
+
+constructor TPathImpl.Create(const Name: UTF8String);
+begin
+  inherited Create();
+  fName := Name;
+  Unify();
+end;
+
+destructor TPathImpl.Destroy();
+begin
+  inherited;
+end;
+
+procedure TPathImpl.Unify();
+var
+  I: integer;
+begin
+  // convert all path delimiters to native ones
+  for I := 1 to Length(fName) do
+  begin
+    if (fName[I] in ['\', '/']) and (fName[I] <> PathDelim) then
+      fName[I] := PathDelim;
+  end;
+end;
+
+function TPathImpl.GetPortableString(): UTF8String;
+var
+  I: integer;
+begin
+  Result := fName;
+  if (PathDelim = '/') then
+    Exit;
+
+  for I := 1 to Length(Result) do
+  begin
+    if (Result[I] = PathDelim) then
+      Result[I] := '/';
+  end;
+end;
+
+function TPathImpl.ToUTF8(UseNativeDelim: boolean): UTF8String;
+begin
+  if (UseNativeDelim) then
+    Result := fName
+  else
+    Result := GetPortableString();
+end;
+
+function TPathImpl.ToWide(UseNativeDelim: boolean): WideString;
+begin
+  if (UseNativeDelim) then
+    Result := UTF8Decode(fName)
+  else
+    Result := UTF8Decode(GetPortableString());
+end;
+
+function TPathImpl.ToNative(): RawByteString;
+begin
+  if (IsNativeUTF8()) then
+    Result := fName
+  else
+    Result := Utf8ToAnsi(fName);
+end;
+
+function TPathImpl.GetDrive(): IPath;
+begin
+  AssertRefCount;
+  Result := FileSystem.ExtractFileDrive(Self);
+end;
+
+function TPathImpl.GetPath(): IPath;
+begin
+  AssertRefCount;
+  Result := FileSystem.ExtractFilePath(Self);
+end;
+
+function TPathImpl.GetDir(): IPath;
+begin
+  AssertRefCount;
+  Result := FileSystem.ExtractFileDir(Self);
+end;
+
+function TPathImpl.GetName(): IPath;
+begin
+  AssertRefCount;
+  Result := FileSystem.ExtractFileName(Self);
+end;
+
+function TPathImpl.GetExtension(): IPath;
+begin
+  AssertRefCount;
+  Result := FileSystem.ExtractFileExt(Self);
+end;
+
+function TPathImpl.SetExtension(const Extension: IPath): IPath;
+begin
+  AssertRefCount;
+  Result := FileSystem.ChangeFileExt(Self, Extension);
+end;
+
+function TPathImpl.GetRelativePath(const BaseName: IPath): IPath;
+begin
+  AssertRefCount;
+  Result := FileSystem.ExtractRelativePath(BaseName, Self);
+end;
+
+function TPathImpl.GetAbsolutePath(): IPath;
+begin
+  AssertRefCount;
+  Result := FileSystem.ExpandFileName(Self);
+end;
+
+function TPathImpl.GetParent(): IPath;
+var
+  CurPath, ParentPath: IPath;
+begin
+  AssertRefCount;
+
+  Result := PathNone;
+
+  CurPath := Self.ExcludeTrailingPathDelimiter();
+  // check if current path has a parent (no further '/')
+  if (Pos(PathDelim, CurPath.ToUTF8()) = 0) then
+    Exit;
+
+  // set new path and check if it has changed to avoid endless loops
+  // e.g. with invalid paths like '/C:' (GetPath() uses ':' as delimiter too)
+  // on delphi/win32
+  ParentPath := CurPath.GetPath();
+  if (ParentPath.ToUTF8 = CurPath.ToUTF8)  then
+    Exit;
+
+  Result := ParentPath;
+end;
+
+function TPathImpl.SplitDirs(): IPathDynArray;
+var
+  CurPath: IPath;
+  TmpPath: IPath;
+  Components: array of IPath;
+  CurPathStr: UTF8String;
+  DelimPos: integer;
+  I: integer;
+begin
+  SetLength(Result, 0);
+
+  if (Length(Self.ToUTF8(true)) = 0) then
+    Exit;
+
+  CurPath := Self;
+  SetLength(Components, 0);
+  repeat
+    SetLength(Components, Length(Components)+1);
+
+    CurPathStr := CurPath.ToUTF8();
+    DelimPos := LastDelimiter(PathDelim, SysUtils.ExcludeTrailingPathDelimiter(CurPathStr));
+    Components[High(Components)] := Path(Copy(CurPathStr, DelimPos+1, Length(CurPathStr)));
+
+    // TODO: remove this workaround for FPC bug
+    TmpPath := CurPath;
+    CurPath := TmpPath.GetParent();
+  until (CurPath = PathNone);
+
+  // reverse list
+  SetLength(Result, Length(Components));
+  for I := 0 to High(Components) do
+    Result[I] := Components[High(Components)-I];
+end;
+
+function TPathImpl.Append(const Child: IPath): IPath;
+begin
+  AssertRefCount;
+
+  if (fName = '') then
+    Result := Child
+  else
+    Result := Path(Self.IncludeTrailingPathDelimiter().ToUTF8() + Child.ToUTF8());
+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();
+  if (FileSystem.IsCaseSensitive() and not IgnoreCase) then
+    Result := (CompareStr(SelfPath, OtherPath) = 0)
+  else
+    Result := (CompareText(SelfPath, OtherPath) = 0);
+end;
+
+function TPathImpl.IsChildOf(const Parent: IPath; Direct: boolean): boolean;
+var
+  SelfPath, ParentPath: UTF8String;
+  TmpPath, TmpPath2: IPath;
+begin
+  Result := false;
+
+  if (Direct) then
+  begin
+    // TODO: remove workaround for fpc refcount bug
+    TmpPath := Self.GetParent();
+    TmpPath2 := TmpPath.GetAbsolutePath();
+    SelfPath := TmpPath2.IncludeTrailingPathDelimiter().ToUTF8();
+
+    // TODO: remove workaround for fpc refcount bug
+    TmpPath := Parent.GetAbsolutePath();
+    ParentPath := TmpPath.IncludeTrailingPathDelimiter().ToUTF8();
+
+    // simply check if this paths parent path (SelfPath) equals ParentPath
+    Result := (SelfPath = ParentPath);
+  end
+  else
+  begin
+    // TODO: remove workaround for fpc refcount bug
+    TmpPath := Self.GetAbsolutePath();
+    SelfPath := TmpPath.IncludeTrailingPathDelimiter().ToUTF8();
+
+    // TODO: remove workaround for fpc refcount bug
+    TmpPath := Parent.GetAbsolutePath();
+    ParentPath := TmpPath.IncludeTrailingPathDelimiter().ToUTF8();
+
+    if (Length(SelfPath) <= Length(ParentPath)) then
+      Exit;
+
+    // check if ParentPath is a substring of SelfPath
+    if (FileSystem.IsCaseSensitive()) then
+      Result := (StrLComp(PAnsiChar(SelfPath), PAnsiChar(ParentPath), Length(ParentPath)) = 0)
+    else
+      Result := (StrLIComp(PAnsiChar(SelfPath), PAnsiChar(ParentPath), Length(ParentPath)) = 0)
+  end;
+end;
+
+function AdjustCaseRecursive(CurPath: IPath; AdjustAllLevels: boolean): IPath;
+var
+  OldParent, AdjustedParent: IPath;
+  TmpPath: IPath;
+  LocalName: IPath;
+  PathFound: IPath;
+  PathWithAdjParent: IPath;
+  SearchInfo: TFileInfo;
+  FileIter: IFileIterator;
+  Pattern: IPath;
+begin
+  // if case-sensitive path exists there is no need to adjust case
+  if (CurPath.Exists()) then
+  begin
+    Result := CurPath;
+    Exit;
+  end;
+
+  // extract name component of current path
+  // TODO: remove workaround for fpc refcount bug
+  TmpPath := CurPath.ExcludeTrailingPathDelimiter();
+  LocalName := TmpPath.GetName();
+
+  // try to adjust parent
+  OldParent := CurPath.GetParent();
+  if (OldParent <> PathNone) then
+  begin
+    if (not AdjustAllLevels) then
+    begin
+      AdjustedParent := OldParent;
+    end
+    else
+    begin
+      AdjustedParent := AdjustCaseRecursive(OldParent, AdjustAllLevels);
+      if (AdjustedParent = nil) then
+      begin
+        // parent path was not found case-insensitive
+        Result := nil;
+        Exit;
+      end;
+
+      // check if the path with adjusted parent can be found now
+      PathWithAdjParent := AdjustedParent.Append(LocalName);
+      if (PathWithAdjParent.Exists()) then
+      begin
+        Result := PathWithAdjParent;
+        Exit;
+      end;
+    end;
+    Pattern := AdjustedParent.Append(Path('*'));
+  end
+  else // path has no parent
+  begin
+    // the top path can either be absolute or relative
+    if (CurPath.IsAbsolute) then
+    begin
+      // the only absolute directory at Unix without a parent is root ('/')
+      // and hence does not need to be adjusted
+      Result := CurPath;
+      Exit;
+    end;
+    // this is a relative path, search in the current working dir
+    AdjustedParent := nil;
+    Pattern := Path('*');
+  end;
+
+  // compare name with all files in the current directory case-insensitive
+  FileIter := FileSystem.FileFind(Pattern, faAnyFile);
+  while (FileIter.HasNext()) do
+  begin
+    SearchInfo := FileIter.Next();
+    PathFound := SearchInfo.Name;
+    if (CompareText(LocalName.ToUTF8, PathFound.ToUTF8) = 0) then
+    begin
+      if (AdjustedParent <> nil) then
+        Result := AdjustedParent.Append(PathFound)
+      else
+        Result := PathFound;
+      Exit;
+    end;
+  end;
+
+  // no matching file found
+  Result := nil;
+end;
+
+function TPathImpl.AdjustCase(AdjustAllLevels: boolean): IPath;
+begin
+  AssertRefCount;
+
+  Result := Self;
+
+  if (FileSystem.IsCaseSensitive) then
+  begin
+    Result := AdjustCaseRecursive(Self, AdjustAllLevels);
+    if (Result = nil) then
+      Result := Self;
+  end;
+end;
+
+function TPathImpl.IncludeTrailingPathDelimiter(): IPath;
+begin
+  AssertRefCount;
+  Result := FileSystem.IncludeTrailingPathDelimiter(Self);
+end;
+
+function TPathImpl.ExcludeTrailingPathDelimiter(): IPath;
+begin
+  AssertRefCount;
+  Result := FileSystem.ExcludeTrailingPathDelimiter(Self);
+end;
+
+function TPathImpl.CreateFile(): THandle;
+begin
+  Result := FileSystem.FileCreate(Self);
+end;
+
+function TPathImpl.CreateDirectory(Force: boolean): boolean;
+begin
+  if (Force) then
+    Result := FileSystem.ForceDirectories(Self)
+  else
+    Result := FileSystem.DirectoryCreate(Self);
+end;
+
+function TPathImpl.Open(Mode: LongWord): THandle;
+begin
+  Result := FileSystem.FileOpen(Self, Mode);
+end;
+
+function TPathImpl.OpenStream(Mode: Word): TUnicodeFileStream;
+begin
+  Result := TUnicodeFileStream.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
+  // note the different specifications of FileExists() on Win32 <> Unix
+  {$IFDEF MSWINDOWS}
+  Result := IsFile() or IsDirectory();
+  {$ELSE}
+  Result := FileSystem.FileExists(Self);
+  {$ENDIF}
+end;
+
+function TPathImpl.IsFile(): boolean;
+begin
+  // note the different specifications of FileExists() on Win32 <> Unix
+  {$IFDEF MSWINDOWS}
+  Result := FileSystem.FileExists(Self);
+  {$ELSE}
+  Result := Exists() and not IsDirectory();
+  {$ENDIF}
+end;
+
+function TPathImpl.IsDirectory(): boolean;
+begin
+  Result := FileSystem.DirectoryExists(Self);
+end;
+
+function TPathImpl.IsAbsolute(): boolean;
+begin
+  AssertRefCount;
+  Result := FileSystem.FileIsReadOnly(Self);
+end;
+
+function TPathImpl.GetAttr(): cardinal;
+begin
+  Result := FileSystem.FileGetAttr(Self);
+end;
+
+function TPathImpl.SetAttr(Attr: Integer): boolean;
+begin
+  Result := FileSystem.FileSetAttr(Self, Attr);
+end;
+
+function TPathImpl.IsReadOnly(): boolean;
+begin
+  Result := FileSystem.FileIsReadOnly(Self);
+end;
+
+function TPathImpl.SetReadOnly(ReadOnly: boolean): boolean;
+begin
+  Result := FileSystem.FileSetReadOnly(Self, ReadOnly);
+end;
+
+function TPathImpl.FileSearch(const DirList: IPath): IPath;
+begin
+  AssertRefCount;
+  Result := FileSystem.FileSearch(Self, DirList);
+end;
+
+function TPathImpl.Rename(const NewName: IPath): boolean;
+begin
+  Result := FileSystem.RenameFile(Self, NewName);
+end;
+
+function TPathImpl.DeleteFile(): boolean;
+begin
+  Result := FileSystem.DeleteFile(Self);
+end;
+
+function TPathImpl.DeleteEmptyDir(): boolean;
+begin
+  Result := FileSystem.RemoveDir(Self);
+end;
+
+function TPathImpl.CopyFile(const Target: IPath; FailIfExists: boolean): boolean;
+begin
+  Result := FileSystem.CopyFile(Self, Target, FailIfExists);
+end;
+
+
+{ TUnicodeFileStream }
+
+{$IFDEF MSWINDOWS}
+
+constructor TUnicodeFileStream.Create(const FileName: IPath; Mode: Word);
+begin
+  inherited Create(FileName.ToWide(), Mode);
+end;
+
+{$ELSE}
+
+constructor TUnicodeFileStream.Create(const FileName: IPath; Mode: Word);
+begin
+  inherited Create(FileName.ToNative(), Mode);
+end;
+
+{$ENDIF}
+
+{ TUnicodeMemoryStream }
+
+procedure TUnicodeMemoryStream.LoadFromFile(const FileName: IPath);
+var
+  Stream: TStream;
+begin
+  Stream := TUnicodeFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
+  try
+    LoadFromStream(Stream);
+  finally
+    Stream.Free;
+  end;
+end;
+
+procedure TUnicodeMemoryStream.SaveToFile(const FileName: IPath);
+var
+  Stream: TStream;
+begin
+  Stream := TUnicodeFileStream.Create(FileName, fmCreate);
+  try
+    SaveToStream(Stream);
+  finally
+    Stream.Free;
+  end;
+end;
+
+var
+  PathNone_Singelton: IPath;
+
+function PathNone(): IPath;
+begin
+  Result := PathNone_Singelton;
+end;
+
+initialization
+  PathNone_Singelton := Path('');
+
+finalization
+  PathNone_Singelton := nil;
+
+end.
diff --git a/unicode/src/base/UUnicodeUtils.pas b/unicode/src/base/UUnicodeUtils.pas
index d34cd432..ce1731b6 100644
--- a/unicode/src/base/UUnicodeUtils.pas
+++ b/unicode/src/base/UUnicodeUtils.pas
@@ -31,14 +31,27 @@ interface
   {$MODE Delphi}
 {$ENDIF}
 
-{$I switches.inc}
-
 uses
 {$IFDEF MSWINDOWS}
   Windows,
 {$ENDIF}
+  StrUtils,
   SysUtils;
-  
+
+type
+  // String with unknown encoding. Introduced with Delphi 2009 and maybe soon
+  // with FPC.
+  RawByteString = AnsiString;
+
+{**
+ * Returns true if the system uses UTF-8 as default string type
+ * (filesystem or API calls).
+ * This is always true on Mac OS X and always false on Win32. On Unix it depends
+ * on the LC_CTYPE setting.
+ * Do not use AnsiToUTF8() or UTF8ToAnsi() if this function returns true.
+ *}
+function IsNativeUTF8(): boolean;
+
 (*
  * Character classes
  *)
@@ -67,7 +80,7 @@ function IsPrintableChar(ch: UCS4Char): boolean; overload;
  * function will most probably return false, as most ANSI strings sequences
  * are illegal in UTF-8.
  *}
-function IsUTF8String(const str: AnsiString): boolean;
+function IsUTF8String(const str: RawByteString): boolean;
 
 {**
  * Iterates over an UTF-8 encoded string.
@@ -87,7 +100,7 @@ procedure UCS4Delete(var Str: UCS4String; Index: Integer; Count: Integer);
 {**
  * Checks if the string is composed of ASCII characters.
  *}
-function IsASCIIString(const str: AnsiString): boolean;
+function IsASCIIString(const str: RawByteString): boolean;
 
 {*
  * String format conversion
@@ -164,6 +177,44 @@ function StringReplaceW(const text : WideString; search, rep: WideChar): WideStr
 
 implementation
 
+{$IFDEF UNIX}
+{$IFNDEF APPLE}
+const
+  LC_CTYPE = 0;
+
+function setlocale(category: integer; locale: PChar): PChar; cdecl; external 'c';
+{$ENDIF}
+{$ENDIF}
+
+var
+  NativeUTF8: boolean;
+
+procedure InitUnicodeUtils();
+{$IFDEF UNIX}
+{$IFNDEF APPLE}
+var
+  localeName: PChar;
+{$ENDIF}
+{$ENDIF}
+begin
+  {$IF Defined(APPLE)}
+    NativeUTF8 := true;
+  {$ELSEIF Defined(MSWindows)}
+    NativeUTF8 := false;
+  {$ELSEIF Defined(UNIX)}
+    // check if locale name contains UTF8 or UTF-8
+    localeName := setlocale(LC_CTYPE, nil);
+    NativeUTF8 := Pos('UTF8', UpperCase(AnsiReplaceStr(localeName, '-', ''))) > 0;
+  {$ELSE}
+    raise Exception.Create('Unknown system');
+  {$IFEND}
+end;
+
+function IsNativeUTF8(): boolean;
+begin
+  Result := NativeUTF8;
+end;
+
 function IsAlphaChar(ch: WideChar): boolean;
 begin
   {$IFDEF MSWINDOWS}
@@ -344,7 +395,7 @@ begin
     Ch := Ord('?');
 end;
 
-function IsUTF8String(const str: AnsiString): boolean;
+function IsUTF8String(const str: RawByteString): boolean;
 var
   Ch: UCS4Char;
   StrPtr: PAnsiChar;
@@ -361,7 +412,7 @@ begin
   end;
 end;
 
-function IsASCIIString(const str: AnsiString): boolean;
+function IsASCIIString(const str: RawByteString): boolean;
 var
   I: integer;
 begin
@@ -514,7 +565,7 @@ begin
     Exit;
   if (Index + Count > Len) then
     Count := Len-Index;
-
+  
   OldStr := Str;
   SetLength(Str, Len-Count+1);
   for I := 0 to Index-1 do
@@ -583,4 +634,7 @@ begin
   end;
 end;
 
+initialization
+  InitUnicodeUtils;
+
 end.
-- 
cgit v1.2.3