(**************************************************************************
QuArK -- Quake Army Knife -- 3D game editor
Copyright (C) QuArK Development Team

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; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

http://quark.sourceforge.net/ - Contact information in AUTHORS.TXT
**************************************************************************)

{
$Header: /cvsroot/quark/source/prog/QkApplPaths.pas,v 1.21 2010/02/23 18:38:23 danielpharos Exp $
 ----------- REVISION HISTORY ------------
$Log: QkApplPaths.pas,v $
Revision 1.21  2010/02/23 18:38:23  danielpharos
Added LOG_SUBDIRECTORY; not set right now.

Revision 1.20  2010/02/16 21:24:34  danielpharos
Added version number split function.

Revision 1.19  2010/02/16 19:56:23  danielpharos
Added option to disable QuArKSAS extractor (and other small, related items).

Revision 1.18  2009/07/15 10:38:00  danielpharos
Updated website link.

Revision 1.17  2009/02/21 17:06:18  danielpharos
Changed all source files to use CRLF text format, updated copyright and GPL text.

Revision 1.16  2009/02/14 17:35:35  danielpharos
You can now "uninstall" gamemodes: just delete the addons-directory of that game. Also, small code changes to accommodate this.

Revision 1.15  2008/12/02 16:17:34  danielpharos
Removed unneeded SetApplicationPath function.

Revision 1.14  2008/11/06 19:29:51  danielpharos
Renamed function to concatenate paths, and start using it.

Revision 1.13  2008/09/20 20:45:27  danielpharos
Added GetQPath functions to Defaults.qrk and Setup.qrk loading.

Revision 1.12  2008/09/20 19:34:43  danielpharos
Const-ed some parameters, and re-factored some code for better performance.

Revision 1.11  2008/05/16 20:57:16  danielpharos
Renamed a Type to avoid possible name-collision

Revision 1.10  2008/02/23 19:25:21  danielpharos
Moved a lot of path/file code around: should make it easier to use

Revision 1.9  2007/03/10 21:56:10  danielpharos
Fixed a backslash-linux problem.

Revision 1.8  2006/11/30 01:21:02  cdunde
To fix for filtering purposes, we do NOT want to use capital letters for cvs.

Revision 1.7  2005/09/28 10:48:31  peter-b
Revert removal of Log and Header keywords

Revision 1.5  2003/08/12 15:42:49  silverpaladin
Added ExtraFunctionality to the uses so that platform independant routines are available for pre-Delphi 6 versions.

Revision 1.4  2003/07/21 04:50:02  nerdiii
Linux compatibility ( '/' '\' )

Revision 1.3  2001/03/20 21:47:10  decker_dk
Updated copyright-header

Revision 1.2  2001/02/02 00:09:32  aiv
Added IsPathDelimiter & IncludeTrailingBackslash to new File : ExtraFunctionality.pas
for us non-D5 users.

Revision 1.1  2001/01/30 19:10:30  decker_dk
Created to control the application-paths and sub-directories in a feasible manner. I'm here thinking about directory-names with spaces in them, and the possible conflict that has on cvs-repository.
}

unit QkApplPaths;

 { ------------------- }

interface

type
  TVersionNumber = array of Integer;

  TQPathType = (
      pQuArK, pQuArKAddon, pQuArKGameAddon, pQuArKDll, pQuArKLog, pQuArKHelp,  //QuArK's own paths
      pUserData, pUserGameData  //The user paths
    );

  { a priority search-order, in which QApplPaths.GetNextPath()
    will return the paths. }
  ApplicationPaths_t =
     (ApplicationRoot
(* For future use:
    , ApplicationUserdata
    , ApplicationUserdataGame
*)
    , ApplicationAddons
    , ApplicationAddonsGame
    , None
    );

  QApplPaths =  class
                  private
                    m_NextPathToTry: ApplicationPaths_t;
                  protected
                    procedure Init();
                  public
                    constructor Create();
                    function GetNextPath(var a_ReturnPath:String) : Boolean;
                end;

function ConvertPath(const S: string): string;
function ReverseSlashes(const S: string): string;
function RemoveTrailingSlash(const Path: String): String;
function ConcatPaths(const Paths: array of String) : String;
function GetQPath(const PathToGet : TQPathType) : String; overload;
function GetQPath(const PathToGet : TQPathType; const GameName: String) : String; overload;
function SplitVersionNumber(const VersionNumber: String; const Delimiter: String = '.') : TVersionNumber;

 { ------------------- }

implementation

uses SysUtils, StrUtils, Windows, Forms, Setup, QkExceptions, ExtraFunctionality;

const
  ADDONS_SUBDIRECTORY = 'addons';
  DLL_SUBDIRECTORY = 'dlls';
  LOG_SUBDIRECTORY = ''; //FIXME: Currently main QuArK directory
  HELP_SUBDIRECTORY = 'help';

var
  ApplicationPath : String; //must always contain trailing backslash

 { ------------------- }

function ConvertPath(const S: string): string;
begin
  {$IFDEF LINUX}
  result:=StringReplace(S,'\',PathDelim,[rfReplaceAll]);
  {$ELSE}
  result:=StringReplace(S,'/',PathDelim,[rfReplaceAll]);
  {$ENDIF}
end;

function ReverseSlashes(const S: string): string;
begin
  {$IFDEF LINUX}
  result:=StringReplace(S,PathDelim,'\',[rfReplaceAll]);
  {$ELSE}
  result:=StringReplace(S,PathDelim,'/',[rfReplaceAll]);
  {$ENDIF}
end;

function RemoveTrailingSlash(const Path: String): String;
begin
  if (RightStr(Path, 1) = '\') or (RightStr(Path, 1) = '/') then
    Result:=LeftStr(Path, Length(Path) - 1)
  else
    Result:=Path;
end;

//You can also send a filename as the last element.
function ConcatPaths(const Paths: array of String) : String;
var
  I: Integer;
  S: String;
begin
  Result:='';
  if High(Paths)=Low(Paths) then
    Exit;
  for I:=Low(Paths) to High(Paths) do
  begin
    S:=Paths[I];
    if Length(S)<>0 then
      if I=High(Paths) then
        Result:=Result+ConvertPath(S)
      else
        Result:=Result+IncludeTrailingPathDelimiter(ConvertPath(S));
  end;
end;

function GetQPath(const PathToGet : TQPathType) : String;
  function UnderscoredGamename : String;
  begin
    { If game-name contains spaces, convert them to underscores. }
    Result:=StringReplace(SetupGameSet.Name,' ','_',[rfReplaceAll]);
  end;
begin
  if ApplicationPath = '' then
    raise InternalE('Error: Application path not yet loaded!');

  case PathToGet of
  pQuArK: Result:=ApplicationPath;
  pQuArKAddon: Result:=ConcatPaths([GetQPath(pQuArK), ADDONS_SUBDIRECTORY]);
  pQuArKGameAddon: Result:=ConcatPaths([GetQPath(pQuArKAddon), UnderscoredGamename]);
  pQuArKDll: Result:=ConcatPaths([GetQPath(pQuArK), DLL_SUBDIRECTORY]);
  pQuArKLog: Result:=ConcatPaths([GetQPath(pQuArK), LOG_SUBDIRECTORY]);
  pQuArKHelp: Result:=ConcatPaths([GetQPath(pQuArK), HELP_SUBDIRECTORY]);
  //FIXME: Currently, these return the same as pQuArKAddon and pQuArKGameAddon.
  //In the future, these should be changed to a my documents path, or even a AppData path!
  pUserData: Result:=ConcatPaths([GetQPath(pQuArK), ADDONS_SUBDIRECTORY]);
  pUserGameData: Result:=ConcatPaths([GetQPath(pQuArKAddon), UnderscoredGamename]);
  end;
  Result:=IncludeTrailingPathDelimiter(Result);
end;

function GetQPath(const PathToGet : TQPathType; const GameName: String) : String;
  function UnderscoredGamename : String;
  begin
    { If game-name contains spaces, convert them to underscores. }
    Result:=StringReplace(SetupSubSet(ssGames, GameName).Name,' ','_',[rfReplaceAll]);
  end;
begin
  if ApplicationPath = '' then
    raise InternalE('Error: Application path not yet loaded!');

  case PathToGet of
  pQuArK: Result:=ApplicationPath;
  pQuArKAddon: Result:=ConcatPaths([GetQPath(pQuArK), ADDONS_SUBDIRECTORY]);
  pQuArKGameAddon: Result:=ConcatPaths([GetQPath(pQuArKAddon), UnderscoredGamename]);
  pQuArKDll: Result:=ConcatPaths([GetQPath(pQuArK), DLL_SUBDIRECTORY]);
  pQuArKHelp: Result:=ConcatPaths([GetQPath(pQuArK), HELP_SUBDIRECTORY]);
  //FIXME: Currently, these return the same as pQuArKAddon and pQuArKGameAddon.
  //In the future, these should be changed to a my documents path, or even a AppData path!
  pUserData: Result:=ConcatPaths([GetQPath(pQuArK), ADDONS_SUBDIRECTORY]);
  pUserGameData: Result:=ConcatPaths([GetQPath(pQuArKAddon), UnderscoredGamename]);
  end;
  Result:=IncludeTrailingPathDelimiter(Result);
end;

 { ------------------- }

constructor QApplPaths.Create();
begin
  Init();
end;

procedure QApplPaths.Init();
begin
  m_NextPathToTry := ApplicationPaths_t(0);
end;

function QApplPaths.GetNextPath(var a_ReturnPath:String) : Boolean;
{ Returns true if there is a path, false if none. }
begin
  case m_NextPathToTry of
  ApplicationRoot:
    a_ReturnPath:=GetQPath(pQuArK);

(* reserved for future use
  ApplicationUserdata:
    a_ReturnPath:=GetQPath(pUserData);
    
  ApplicationUserdataGame:
    a_ReturnPath:=GetQPath(pUserGameData);
*)

  ApplicationAddons:
    a_ReturnPath:=GetQPath(pQuArKAddon);

  ApplicationAddonsGame:
    a_ReturnPath:=GetQPath(pQuArKGameAddon);

  else
    Init();
    Result:=False;
    a_ReturnPath:='';
    Exit;
  end;

  Result:=True;
  Inc(m_NextPathToTry);
end;

function SplitVersionNumber(const VersionNumber: String; const Delimiter: String): TVersionNumber;
var
  Index: Integer;
  OldIndex: Integer;
begin
  SetLength(Result, 0);
  Index:=Pos(Delimiter, VersionNumber);
  if Index=0 then
  begin
    //No delimiter found
    SetLength(Result, 1);
    Result[0]:=StrToIntDef(VersionNumber, 0);
    Exit;
  end;
  OldIndex:=1;
  while (Index > 0) do
  begin
    SetLength(Result, Length(Result)+1);
    Result[Length(Result)-1]:=StrToIntDef(MidStr(VersionNumber, OldIndex, Index - OldIndex), 0);
    OldIndex:=Index+1;
    Index:=PosEx(Delimiter, VersionNumber, OldIndex);
  end;
  SetLength(Result, Length(Result)+1);
  Result[Length(Result)-1]:=StrToIntDef(RightStr(VersionNumber, Length(VersionNumber) - OldIndex + 1), 0);
end;

initialization
  ApplicationPath := GetEnvironmentVariable('QUARKPATH');
  if ApplicationPath = '' then
    ApplicationPath := ExtractFilePath(Application.Exename);
  ApplicationPath := IncludeTrailingPathDelimiter(ApplicationPath);
end.
