Jelajahi Sumber

Update the FileIO Aliases (#4186)

* Update the FileIOAlias naming to make the cache, project root and engine
root paths more clear

The alias of `@root@`, `@assets@`, and `@projectplatformcache@` has been
collapsed to `@projectproductassets@`

The alias of `@devroot@` and `@engroot@` has been collapsed to
`@engroot@`

The alias of `@devassets@` and `@projectroot@` has been collapsed to
`@projectroot@`

Signed-off-by: lumberyard-employee-dm <[email protected]>

* Updated use of devassets and devroot properties in python

Those properties now use projectroot and engroot

Signed-off-by: lumberyard-employee-dm <[email protected]>

* Updating the alias @engroot@ alias path comment in each platform specific LocalFileIO_*.cpp file

Signed-off-by: lumberyard-employee-dm <[email protected]>

* Removed hardcoded size of 9 for the product asset alias.

The ResolvePath function now just appends the @projectproductassets@
alias with the input path

Signed-off-by: lumberyard-employee-dm <[email protected]>

* Remove duplicate @projectproductassets@ check in ProcessFileTreeRequest

Signed-off-by: lumberyard-employee-dm <[email protected]>

* Fix for typos in Hydra python test

Signed-off-by: lumberyard-employee-dm <[email protected]>

* Updated LocalFileIO::Copy call on Windows to use the Unicode aware CopyFileW API

Signed-off-by: lumberyard-employee-dm <[email protected]>

* Updated the AWSMetreicsGemAllocatorFixture to properly suppress asset
cache write errors for Test file creation.

Signed-off-by: lumberyard-employee-dm <[email protected]>

* Removed unneeded call to set the @projectproductasstes@ alias at the bottom of the AssetSeedManagerTest SetUp

Signed-off-by: lumberyard-employee-dm <[email protected]>

* Added a deprecated alias map to the FileIO System

When a deprecated alias is accessed, the FileIO System logs an AZ_Error and indicates the alias that should be used

Signed-off-by: lumberyard-employee-dm <[email protected]>

* Updated python test scripts to use the projectroot binding

Retrieving the AutomatedTesting project path based on "<devroot>/AutomatedTesting" has been removed.

Signed-off-by: lumberyard-employee-dm <[email protected]>

* Updated references to devroot and devgame within the codebase

The GetAbsoluteDevGameFolderPath functions has been replaced with direct call to AZ::Utils::GetProjectPath
The GetAbsoluteDevRootFolderPath functions has been replaced with direct calls to AZ::Utils::GetEnginePath

Signed-off-by: lumberyard-employee-dm <[email protected]>

* Updated <engroot>/AutomatedTesting references to projectroot


Signed-off-by: lumberyard-employee-dm <[email protected]>

* Replaced references that assumes the project path is <engroot>/AutomatedTesting with <projectroot> in the AutomatedTesting python test

Signed-off-by: lumberyard-employee-dm <[email protected]>

* Correct casing in emfxworkspace file


Signed-off-by: lumberyard-employee-dm <[email protected]>

* Removed newly added AppendPathParts function
Removed the Path constructors which accepts a PathIterable instance

The PathIterable isn't safe to return to a user of the Path class as it might be referencing temporary paths supplied via PathView arguments

Signed-off-by: lumberyard-employee-dm <[email protected]>

* Fixed unused parameter warning

Signed-off-by: lumberyard-employee-dm <[email protected]>

* Undid change to the LexicallyProximate function to set the path result to the base path.

It needs to return the *this path if the pathResult is empty

Signed-off-by: lumberyard-employee-dm <[email protected]>

* Moved the LocalFileIO ConvertToAbsolutePath implementations to AZ::Utils

Fixed the ConvertToAbsolutePath implementation for Unix platforms to use a buffer that is size PATH_MAX(4096 on all our supported Unix platforms).
Because the buffer before was AZ::IO::MaxPathLength which as a size of 1024, this was resulting in the Unix `realpath` function smashing the stack when it converts a path to an absolute path that is longer than 1024 characters

Signed-off-by: lumberyard-employee-dm <[email protected]>

* Updated the EditorCore.Tests to attach the AZ Environment to the EditorCore shared library that is statically loaded on launch.

Signed-off-by: lumberyard-employee-dm <[email protected]>

* Fixed for DeprecatedAlaisesKeyVisitor Visit function causing the non string_view overloads being hidden causing a hidden base class function warning

Signed-off-by: lumberyard-employee-dm <[email protected]>

* Changed the AWSMetricsGemMock to use a temporary for writing test assets

Signed-off-by: lumberyard-employee-dm <[email protected]>

* Updated the LocalFileIO::ResolvePath function to use HasRootPath to determine if a path starts with a root path such as "D:", "D:/" or "/"

IsAbsolute was not the corect check as the path of "D:" is a relative
path.
To be absolute according to the Windows the path must have a root
directory. i.e "D:/" or "D:\"

Signed-off-by: lumberyard-employee-dm <[email protected]>

* Removed absolute path comment from LocalFile_UnixLike.cpp and LocalFile_Android.cpp FindFiles implementations
Updated the ConvertToAlias to supply an AZ::IO::FixedMaxPath

Signed-off-by: lumberyard-employee-dm <[email protected]>

* Replaced usage of the @projectproductassets@ alias with @engroot@ when referring to the LmbrCentral source folder in the CopyDependencyBuilderTest and the SeedBuilderTests

Signed-off-by: lumberyard-employee-dm <[email protected]>

* Updated the ScriptCanvas Upgrade Tool to output backed up files to the
Project User Path instead of the engine root

Signed-off-by: lumberyard-employee-dm <[email protected]>

* Fixed whitespacing issues in Application.cpp

Signed-off-by: lumberyard-employee-dm <[email protected]>

* Remove unnecessary creation of a FixedMaxPath in the UpgradeTool.cpp

Signed-off-by: lumberyard-employee-dm <[email protected]>

* Modified testSeedFolder variable in the SeedBuilderTests to use the
@engroot@ alias instead of @projectproductassets@/.. alias when
referring to the LmbrCentral Gem source path

Signed-off-by: lumberyard-employee-dm <[email protected]>

* Updated references to the Project Asset Cache in the PythonTests.

Those tests no longer use the logic `azlmbr.paths.projectroot / "Cache" / "pc"` to retrieve a path to the cache root but instead the `azlmbr.paths.projectproductassets` constant

Signed-off-by: lumberyard-employee-dm <[email protected]>

* Fixed the FileIO Deprecated Alias test on Windows

Signed-off-by: lumberyard-employee-dm <[email protected]>

* Removing @projectsourceassets@ alias, as it is only used once.

Updated the PhysX EditorSystemComponent.cpp to query the ProjectPath
form the SettingsRegistry.

Signed-off-by: lumberyard-employee-dm <[email protected]>

* Replaced @projectproductassets@ alias with @products@

Signed-off-by: lumberyard-employee-dm <[email protected]>

* Rollback changes to the PhysX EditorSystemComponent.cpp

The changes to use the ProjectPath from the SettingsRegistry has been implemented in PR #4497

Signed-off-by: lumberyard-employee-dm <[email protected]>
lumberyard-employee-dm 3 tahun lalu
induk
melakukan
f648cb1fd8
100 mengubah file dengan 820 tambahan dan 1045 penghapusan
  1. 0 32
      Assets/Engine/exclude.filetag
  2. 1 1
      AutomatedTesting/Gem/PythonTests/Atom/atom_utils/material_editor_utils.py
  3. 1 1
      AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_AddedToEntity.py
  4. 1 1
      AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_LightComponent.py
  5. 8 8
      AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomMaterialEditor_BasicTests.py
  6. 1 1
      AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_GPUTest_AtomFeatureIntegrationBenchmark.py
  7. 1 1
      AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_GPUTest_BasicLevelSetup.py
  8. 1 1
      AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_GPUTest_LightComponent.py
  9. 0 1
      AutomatedTesting/Gem/PythonTests/EditorPythonBindings/EditorCommandLine_test.py
  10. 0 6
      AutomatedTesting/Gem/PythonTests/EditorPythonBindings/EditorCommandLine_test_case.py
  11. 1 1
      AutomatedTesting/Gem/PythonTests/EditorPythonBindings/EditorScripts/ComponentUpdateListProperty_test_case.py
  12. 1 1
      AutomatedTesting/Gem/PythonTests/EditorPythonBindings/LevelComponentCommands.cfg
  13. 1 1
      AutomatedTesting/Gem/PythonTests/EditorPythonBindings/ViewportTitleDlgCommands.cfg
  14. 1 1
      AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/DynamicSliceInstanceSpawner_Embedded_E2E.py
  15. 1 1
      AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/DynamicSliceInstanceSpawner_External_E2E.py
  16. 1 1
      AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/LayerBlender_E2E_Editor.py
  17. 3 3
      AutomatedTesting/Gem/PythonTests/largeworlds/large_worlds_utils/editor_dynveg_test_helper.py
  18. 1 1
      AutomatedTesting/Levels/Physics/Material_DefaultLibraryConsistentOnAllFeatures/cowboy.emfxworkspace
  19. 1 1
      AutomatedTesting/Levels/Physics/Material_DefaultMaterialLibraryChangesWork/ws.emfxworkspace
  20. 6 7
      Code/Editor/Controls/FolderTreeCtrl.cpp
  21. 2 0
      Code/Editor/Core/Tests/test_Main.cpp
  22. 22 15
      Code/Editor/Core/Tests/test_PathUtil.cpp
  23. 1 1
      Code/Editor/CryEdit.cpp
  24. 2 2
      Code/Editor/CryEditDoc.cpp
  25. 1 1
      Code/Editor/Dialogs/PythonScriptsDialog.cpp
  26. 11 25
      Code/Editor/EditorFileMonitor.cpp
  27. 1 1
      Code/Editor/Plugins/ComponentEntityEditorPlugin/SandboxIntegration.cpp
  28. 24 55
      Code/Editor/Plugins/EditorAssetImporter/AssetImporterWindow.cpp
  29. 10 15
      Code/Editor/Plugins/EditorAssetImporter/SceneSerializationHandler.cpp
  30. 0 1
      Code/Editor/Plugins/ProjectSettingsTool/ProjectSettingsToolWindow.cpp
  31. 0 1
      Code/Editor/Plugins/ProjectSettingsTool/ProjectSettingsToolWindow.h
  32. 14 20
      Code/Editor/Plugins/ProjectSettingsTool/Utils.cpp
  33. 1 1
      Code/Editor/Plugins/ProjectSettingsTool/Utils.h
  34. 3 2
      Code/Editor/Settings.cpp
  35. 13 13
      Code/Editor/TrackView/SequenceBatchRenderDialog.cpp
  36. 33 167
      Code/Editor/Util/PathUtil.cpp
  37. 0 90
      Code/Editor/Util/PathUtil.h
  38. 10 5
      Code/Framework/AzCore/AzCore/IO/FileIO.h
  39. 16 16
      Code/Framework/AzCore/AzCore/IO/IStreamer.h
  40. 31 21
      Code/Framework/AzCore/AzCore/IO/Path/Path.h
  41. 59 24
      Code/Framework/AzCore/AzCore/IO/Path/Path.inl
  42. 1 1
      Code/Framework/AzCore/AzCore/IO/Path/PathIterable.inl
  43. 3 3
      Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.cpp
  44. 1 0
      Code/Framework/AzCore/AzCore/UnitTest/Mocks/MockFileIOBase.h
  45. 14 0
      Code/Framework/AzCore/AzCore/Utils/Utils.cpp
  46. 1 0
      Code/Framework/AzCore/AzCore/Utils/Utils.h
  47. 22 11
      Code/Framework/AzCore/Platform/Android/AzCore/Utils/Utils_Android.cpp
  48. 20 11
      Code/Framework/AzCore/Platform/Common/UnixLike/AzCore/Utils/Utils_UnixLike.cpp
  49. 5 12
      Code/Framework/AzCore/Platform/Common/WinAPI/AzCore/Utils/Utils_WinAPI.cpp
  50. 4 0
      Code/Framework/AzCore/Tests/FileIOBaseTestTypes.h
  51. 1 1
      Code/Framework/AzCore/Tests/IO/Path/PathTests.cpp
  52. 77 12
      Code/Framework/AzFramework/AzFramework/Application/Application.cpp
  53. 3 3
      Code/Framework/AzFramework/AzFramework/Archive/Archive.cpp
  54. 10 0
      Code/Framework/AzFramework/AzFramework/Archive/ArchiveFileIO.cpp
  55. 1 0
      Code/Framework/AzFramework/AzFramework/Archive/ArchiveFileIO.h
  56. 9 9
      Code/Framework/AzFramework/AzFramework/Archive/ArchiveFindData.cpp
  57. 1 1
      Code/Framework/AzFramework/AzFramework/Archive/MissingFileReport.cpp
  58. 1 1
      Code/Framework/AzFramework/AzFramework/Asset/AssetCatalog.cpp
  59. 1 1
      Code/Framework/AzFramework/AzFramework/Gem/GemInfo.cpp
  60. 89 30
      Code/Framework/AzFramework/AzFramework/IO/LocalFileIO.cpp
  61. 4 2
      Code/Framework/AzFramework/AzFramework/IO/LocalFileIO.h
  62. 22 8
      Code/Framework/AzFramework/AzFramework/IO/RemoteFileIO.cpp
  63. 2 0
      Code/Framework/AzFramework/AzFramework/IO/RemoteFileIO.h
  64. 13 49
      Code/Framework/AzFramework/Platform/Android/AzFramework/IO/LocalFileIO_Android.cpp
  65. 12 38
      Code/Framework/AzFramework/Platform/Common/UnixLike/AzFramework/IO/LocalFileIO_UnixLike.cpp
  66. 1 32
      Code/Framework/AzFramework/Platform/Common/WinAPI/AzFramework/IO/LocalFileIO_WinAPI.cpp
  67. 15 17
      Code/Framework/AzFramework/Platform/Windows/AzFramework/IO/LocalFileIO_Windows.cpp
  68. 1 1
      Code/Framework/AzFramework/Tests/Application.cpp
  69. 31 31
      Code/Framework/AzFramework/Tests/ArchiveTests.cpp
  70. 45 0
      Code/Framework/AzFramework/Tests/FileIO.cpp
  71. 9 9
      Code/Framework/AzFramework/Tests/FileTagTests.cpp
  72. 41 69
      Code/Framework/AzFramework/Tests/GenAppDescriptors.cpp
  73. 2 2
      Code/Framework/AzGameFramework/AzGameFramework/Application/GameApplication.cpp
  74. 1 1
      Code/Framework/AzNetworking/AzNetworking/Utilities/EncryptionCommon.cpp
  75. 0 8
      Code/Framework/AzToolsFramework/AzToolsFramework/API/EditorAssetSystemAPI.h
  76. 0 20
      Code/Framework/AzToolsFramework/AzToolsFramework/Asset/AssetSystemComponent.cpp
  77. 0 2
      Code/Framework/AzToolsFramework/AzToolsFramework/Asset/AssetSystemComponent.h
  78. 1 1
      Code/Framework/AzToolsFramework/AzToolsFramework/AssetEditor/AssetEditorWidget.cpp
  79. 1 1
      Code/Framework/AzToolsFramework/AzToolsFramework/Debug/TraceContext.h
  80. 1 1
      Code/Framework/AzToolsFramework/AzToolsFramework/Logger/TraceLogger.cpp
  81. 3 6
      Code/Framework/AzToolsFramework/AzToolsFramework/Slice/SliceRequestComponent.cpp
  82. 3 3
      Code/Framework/AzToolsFramework/AzToolsFramework/Slice/SliceTransaction.cpp
  83. 2 2
      Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/EditorLayerComponent.cpp
  84. 1 1
      Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp
  85. 2 2
      Code/Framework/AzToolsFramework/Tests/ArchiveTests.cpp
  86. 15 19
      Code/Framework/AzToolsFramework/Tests/AssetSeedManager.cpp
  87. 0 2
      Code/Framework/AzToolsFramework/Tests/AssetSystemMocks.h
  88. 2 2
      Code/Framework/AzToolsFramework/Tests/EntityTestbed.h
  89. 4 4
      Code/Framework/AzToolsFramework/Tests/PlatformAddressedAssetCatalogTests.cpp
  90. 0 2
      Code/Framework/AzToolsFramework/Tests/SliceStabilityTests/SliceStabilityTestFramework.h
  91. 3 3
      Code/Legacy/CryCommon/CryPath.h
  92. 3 3
      Code/Legacy/CrySystem/ConsoleBatchFile.cpp
  93. 1 1
      Code/Legacy/CrySystem/DebugCallStack.cpp
  94. 1 1
      Code/Legacy/CrySystem/LevelSystem/LevelSystem.cpp
  95. 5 5
      Code/Legacy/CrySystem/SystemCFG.cpp
  96. 3 6
      Code/Legacy/CrySystem/SystemInit.cpp
  97. 1 1
      Code/Tools/AWSNativeSDKInit/source/Platform/Android/InitializeCerts_Android.cpp
  98. 6 11
      Code/Tools/AssetProcessor/AssetBuilder/AssetBuilderComponent.cpp
  99. 14 32
      Code/Tools/AssetProcessor/native/AssetManager/AssetCatalog.cpp
  100. 0 4
      Code/Tools/AssetProcessor/native/AssetManager/AssetCatalog.h

+ 0 - 32
Assets/Engine/exclude.filetag

@@ -125,17 +125,6 @@
 					<Class name="AZStd::string" field="Comment" value="" type="{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9}"/>
 				</Class>
 			</Class>
-			<Class name="AZStd::pair" field="element" type="{941B5626-118F-55BC-925E-6E416A7520E4}">
-				<Class name="AZStd::string" field="value1" value="@devroot@/*.waf_files" type="{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9}"/>
-				<Class name="FileTagData" field="value2" version="2" type="{5F66E43B-548B-4AA8-8CD8-F6924F6031E6}">
-					<Class name="unsigned char" field="FilePatternType" value="1" type="{72B9409A-7D1A-4831-9CFE-FCB3FADD3426}"/>
-					<Class name="AZStd::set" field="FileTags" type="{166F208E-DE97-53FE-B349-BDD9FE9B8693}">
-						<Class name="AZStd::string" field="element" value="ignore" type="{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9}"/>
-						<Class name="AZStd::string" field="element" value="productdependency" type="{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9}"/>
-					</Class>
-					<Class name="AZStd::string" field="Comment" value="" type="{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9}"/>
-				</Class>
-			</Class>
 			<Class name="AZStd::pair" field="element" type="{941B5626-118F-55BC-925E-6E416A7520E4}">
 				<Class name="AZStd::string" field="value1" value="*/editor/leveltemplates.xml" type="{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9}"/>
 				<Class name="FileTagData" field="value2" version="2" type="{5F66E43B-548B-4AA8-8CD8-F6924F6031E6}">
@@ -207,27 +196,6 @@
 					<Class name="AZStd::string" field="Comment" value="" type="{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9}"/>
 				</Class>
 			</Class>
-			<Class name="AZStd::pair" field="element" type="{941B5626-118F-55BC-925E-6E416A7520E4}">
-				<Class name="AZStd::string" field="value1" value="@devroot@/*wscript" type="{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9}"/>
-				<Class name="FileTagData" field="value2" version="2" type="{5F66E43B-548B-4AA8-8CD8-F6924F6031E6}">
-					<Class name="unsigned char" field="FilePatternType" value="1" type="{72B9409A-7D1A-4831-9CFE-FCB3FADD3426}"/>
-					<Class name="AZStd::set" field="FileTags" type="{166F208E-DE97-53FE-B349-BDD9FE9B8693}">
-						<Class name="AZStd::string" field="element" value="ignore" type="{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9}"/>
-						<Class name="AZStd::string" field="element" value="productdependency" type="{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9}"/>
-					</Class>
-					<Class name="AZStd::string" field="Comment" value="" type="{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9}"/>
-				</Class>
-			</Class>
-			<Class name="AZStd::pair" field="element" type="{941B5626-118F-55BC-925E-6E416A7520E4}">
-				<Class name="AZStd::string" field="value1" value=".*/assetprocessorplatformconfig.ini" type="{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9}"/>
-				<Class name="FileTagData" field="value2" version="2" type="{5F66E43B-548B-4AA8-8CD8-F6924F6031E6}">
-					<Class name="unsigned char" field="FilePatternType" value="2" type="{72B9409A-7D1A-4831-9CFE-FCB3FADD3426}"/>
-					<Class name="AZStd::set" field="FileTags" type="{166F208E-DE97-53FE-B349-BDD9FE9B8693}">
-						<Class name="AZStd::string" field="element" value="editoronly" type="{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9}"/>
-					</Class>
-					<Class name="AZStd::string" field="Comment" value="" type="{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9}"/>
-				</Class>
-			</Class>
 			<Class name="AZStd::pair" field="element" type="{941B5626-118F-55BC-925E-6E416A7520E4}">
 				<Class name="AZStd::string" field="value1" value=".*/gems?/?.*/gem.json" type="{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9}"/>
 				<Class name="FileTagData" field="value2" version="2" type="{5F66E43B-548B-4AA8-8CD8-F6924F6031E6}">

+ 1 - 1
AutomatedTesting/Gem/PythonTests/Atom/atom_utils/material_editor_utils.py

@@ -211,7 +211,7 @@ class Timeout:
         return time.time() > self.die_after
 
 
-screenshotsFolder = os.path.join(azlmbr.paths.devroot, "AtomTest", "Cache" "pc", "Screenshots")
+screenshotsFolder = os.path.join(azlmbr.paths.products, "Screenshots")
 
 
 class ScreenshotHelper:

+ 1 - 1
AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_AddedToEntity.py

@@ -17,7 +17,7 @@ import azlmbr.legacy.general as general
 import azlmbr.editor as editor
 import azlmbr.render as render
 
-sys.path.append(os.path.join(azlmbr.paths.devroot, "AutomatedTesting", "Gem", "PythonTests"))
+sys.path.append(os.path.join(azlmbr.paths.projectroot, "Gem", "PythonTests"))
 
 import editor_python_test_tools.hydra_editor_utils as hydra
 from editor_python_test_tools.utils import TestHelper

+ 1 - 1
AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomEditorComponents_LightComponent.py

@@ -14,7 +14,7 @@ import azlmbr.math as math
 import azlmbr.paths
 import azlmbr.legacy.general as general
 
-sys.path.append(os.path.join(azlmbr.paths.devassets, "Gem", "PythonTests"))
+sys.path.append(os.path.join(azlmbr.paths.projectroot, "Gem", "PythonTests"))
 
 import editor_python_test_tools.hydra_editor_utils as hydra
 from Atom.atom_utils.atom_constants import LIGHT_TYPES

+ 8 - 8
AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomMaterialEditor_BasicTests.py

@@ -16,7 +16,7 @@ import time
 import azlmbr.math as math
 import azlmbr.paths
 
-sys.path.append(os.path.join(azlmbr.paths.devassets, "Gem", "PythonTests"))
+sys.path.append(os.path.join(azlmbr.paths.projectroot, "Gem", "PythonTests"))
 
 import Atom.atom_utils.material_editor_utils as material_editor
 
@@ -27,10 +27,10 @@ TEST_MATERIAL_1 = "001_DefaultWhite.material"
 TEST_MATERIAL_2 = "002_BaseColorLerp.material"
 TEST_MATERIAL_3 = "003_MetalMatte.material"
 TEST_DATA_PATH = os.path.join(
-    azlmbr.paths.devroot, "Gems", "Atom", "TestData", "TestData", "Materials", "StandardPbrTestCases"
+    azlmbr.paths.engroot, "Gems", "Atom", "TestData", "TestData", "Materials", "StandardPbrTestCases"
 )
 MATERIAL_TYPE_PATH = os.path.join(
-    azlmbr.paths.devroot, "Gems", "Atom", "Feature", "Common", "Assets",
+    azlmbr.paths.engroot, "Gems", "Atom", "Feature", "Common", "Assets",
     "Materials", "Types", "StandardPBR.materialtype",
 )
 CACHE_FILE_EXTENSION = ".azmaterial"
@@ -61,7 +61,7 @@ def run():
     print(f"Material opened: {material_editor.is_open(document_id)}")
 
     # Verify if the test material exists initially
-    target_path = os.path.join(azlmbr.paths.devroot, "AutomatedTesting", "Materials", NEW_MATERIAL)
+    target_path = os.path.join(azlmbr.paths.projectroot, "Materials", NEW_MATERIAL)
     print(f"Test asset doesn't exist initially: {not os.path.exists(target_path)}")
 
     # 2) Test Case: Creating a New Material Using Existing One
@@ -109,10 +109,10 @@ def run():
     # Assign new color to the material file and save the document as copy
     expected_color_1 = math.Color(0.5, 0.5, 0.5, 1.0)
     material_editor.set_property(document_id, property_name, expected_color_1)
-    target_path_1 = os.path.join(azlmbr.paths.devroot, "AutomatedTesting", "Materials", NEW_MATERIAL_1)
+    target_path_1 = os.path.join(azlmbr.paths.projectroot, "Materials", NEW_MATERIAL_1)
     cache_file_name_1 = os.path.splitext(NEW_MATERIAL_1)  # Example output: ('test_material_1', '.material')
     cache_file_1 = f"{cache_file_name_1[0]}{CACHE_FILE_EXTENSION}"
-    target_path_1_cache = os.path.join(azlmbr.paths.devassets, "Cache", "pc", "materials", cache_file_1)
+    target_path_1_cache = os.path.join(azlmbr.paths.products, "materials", cache_file_1)
     material_editor.save_document_as_copy(document_id, target_path_1)
     material_editor.wait_for_condition(lambda: os.path.exists(target_path_1_cache), 4.0)
 
@@ -120,10 +120,10 @@ def run():
     # Assign new color to the material file save the document as child
     expected_color_2 = math.Color(0.75, 0.75, 0.75, 1.0)
     material_editor.set_property(document_id, property_name, expected_color_2)
-    target_path_2 = os.path.join(azlmbr.paths.devroot, "AutomatedTesting", "Materials", NEW_MATERIAL_2)
+    target_path_2 = os.path.join(azlmbr.paths.projectroot, "Materials", NEW_MATERIAL_2)
     cache_file_name_2 = os.path.splitext(NEW_MATERIAL_1)  # Example output: ('test_material_2', '.material')
     cache_file_2 = f"{cache_file_name_2[0]}{CACHE_FILE_EXTENSION}"
-    target_path_2_cache = os.path.join(azlmbr.paths.devassets, "Cache", "pc", "materials", cache_file_2)
+    target_path_2_cache = os.path.join(azlmbr.paths.products, "materials", cache_file_2)
     material_editor.save_document_as_child(document_id, target_path_2)
     material_editor.wait_for_condition(lambda: os.path.exists(target_path_2_cache), 4.0)
 

+ 1 - 1
AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_GPUTest_AtomFeatureIntegrationBenchmark.py

@@ -10,7 +10,7 @@ import sys
 
 import azlmbr.legacy.general as general
 
-sys.path.append(os.path.join(azlmbr.paths.devroot, "AutomatedTesting", "Gem", "PythonTests"))
+sys.path.append(os.path.join(azlmbr.paths.projectroot, "Gem", "PythonTests"))
 
 import editor_python_test_tools.hydra_editor_utils as hydra
 from editor_python_test_tools.editor_test_helper import EditorTestHelper

+ 1 - 1
AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_GPUTest_BasicLevelSetup.py

@@ -17,7 +17,7 @@ import azlmbr.math as math
 import azlmbr.paths
 import azlmbr.editor as editor
 
-sys.path.append(os.path.join(azlmbr.paths.devroot, "AutomatedTesting", "Gem", "PythonTests"))
+sys.path.append(os.path.join(azlmbr.paths.projectroot, "Gem", "PythonTests"))
 
 import editor_python_test_tools.hydra_editor_utils as hydra
 from editor_python_test_tools.editor_test_helper import EditorTestHelper

+ 1 - 1
AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_GPUTest_LightComponent.py

@@ -14,7 +14,7 @@ import azlmbr.math as math
 import azlmbr.paths
 import azlmbr.legacy.general as general
 
-sys.path.append(os.path.join(azlmbr.paths.devroot, "AutomatedTesting", "Gem", "PythonTests"))
+sys.path.append(os.path.join(azlmbr.paths.projectroot, "Gem", "PythonTests"))
 
 import editor_python_test_tools.hydra_editor_utils as hydra
 from Atom.atom_utils import atom_component_helper, atom_constants, screenshot_utils

+ 0 - 1
AutomatedTesting/Gem/PythonTests/EditorPythonBindings/EditorCommandLine_test.py

@@ -42,7 +42,6 @@ class TestEditorAutomation(object):
             "editor command line arg bar",
             "editor command line arg baz",
             "editor engroot set",
-            "editor devroot set",
             "path resolved worked"
             ]
         

+ 0 - 6
AutomatedTesting/Gem/PythonTests/EditorPythonBindings/EditorCommandLine_test_case.py

@@ -20,12 +20,6 @@ if (engroot is not None and len(engroot) is not 0):
     print ('engroot is {}'.format(engroot))
     print ('editor engroot set')
 
-# make sure the @devroot@ exists as a azlmbr.paths property
-devroot = azlmbr.paths.devroot
-if (devroot is not None and len(devroot) != 0):
-    print ('devroot is {}'.format(devroot))
-    print ('editor devroot set')
-
 # resolving a basic path
 path = azlmbr.paths.resolve_path('@engroot@/engineassets/texturemsg/defaultsolids.mtl')
 if (len(path) != 0 and path.find('@engroot@') == -1):

+ 1 - 1
AutomatedTesting/Gem/PythonTests/EditorPythonBindings/EditorScripts/ComponentUpdateListProperty_test_case.py

@@ -16,7 +16,7 @@ import azlmbr.entity as entity
 import azlmbr.math as math
 import azlmbr.paths
 
-sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests'))
+sys.path.append(os.path.join(azlmbr.paths.projectroot, 'Gem', 'PythonTests'))
 from automatedtesting_shared.editor_test_helper import EditorTestHelper
 
 

+ 1 - 1
AutomatedTesting/Gem/PythonTests/EditorPythonBindings/LevelComponentCommands.cfg

@@ -1,2 +1,2 @@
 # this file is copied to $/dev/editor_autoexec.cfg so the the Editor automation runs for this Hydra test
-pyRunFile @devroot@/Tests/hydra/LevelComponentCommands_test_case.py exit_when_done
+pyRunFile @engroot@/Tests/hydra/LevelComponentCommands_test_case.py exit_when_done

+ 1 - 1
AutomatedTesting/Gem/PythonTests/EditorPythonBindings/ViewportTitleDlgCommands.cfg

@@ -1,2 +1,2 @@
 # this file is copied to $/dev/editor_autoexec.cfg so the the Editor automation runs for this Hydra test
-pyRunFile @devroot@/Tests/hydra/ViewportTitleDlgCommands_test_case.py
+pyRunFile @engroot@/Tests/hydra/ViewportTitleDlgCommands_test_case.py

+ 1 - 1
AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/DynamicSliceInstanceSpawner_Embedded_E2E.py

@@ -109,7 +109,7 @@ def DynamicSliceInstanceSpawner_Embedded_E2E():
     # 6) Save and export to engine
     general.save_level()
     general.export_to_engine()
-    pak_path = os.path.join(paths.devroot, "AutomatedTesting", "cache", "pc", "levels", lvl_name, "level.pak")
+    pak_path = os.path.join(paths.products, "levels", lvl_name, "level.pak")
     Report.result(Tests.saved_and_exported, os.path.exists(pak_path))
 
 

+ 1 - 1
AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/DynamicSliceInstanceSpawner_External_E2E.py

@@ -131,7 +131,7 @@ def DynamicSliceInstanceSpawner_External_E2E():
     # 6) Save and export to engine
     general.save_level()
     general.export_to_engine()
-    pak_path = os.path.join(paths.devroot, "AutomatedTesting", "cache", "pc", "levels", lvl_name, "level.pak")
+    pak_path = os.path.join(paths.products, "levels", lvl_name, "level.pak")
     Report.result(Tests.saved_and_exported, os.path.exists(pak_path))
 
 

+ 1 - 1
AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/LayerBlender_E2E_Editor.py

@@ -155,7 +155,7 @@ def LayerBlender_E2E_Editor():
     # 6) Save and export to engine
     general.save_level()
     general.export_to_engine()
-    pak_path = os.path.join(paths.devroot, "AutomatedTesting", "cache", "pc", "levels", lvl_name, "level.pak")
+    pak_path = os.path.join(paths.products, "levels", lvl_name, "level.pak")
     Report.result(Tests.saved_and_exported, os.path.exists(pak_path))
 
 

+ 3 - 3
AutomatedTesting/Gem/PythonTests/largeworlds/large_worlds_utils/editor_dynveg_test_helper.py

@@ -17,7 +17,7 @@ import azlmbr.vegetation as vegetation
 import azlmbr.areasystem as areasystem
 import azlmbr.paths
 
-sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests'))
+sys.path.append(os.path.join(azlmbr.paths.projectroot, 'Gem', 'PythonTests'))
 import editor_python_test_tools.hydra_editor_utils as hydra
 
 
@@ -25,7 +25,7 @@ def create_surface_entity(name, center_point, box_size_x, box_size_y, box_size_z
     # Create a "flat surface" entity to use as a plantable vegetation surface
     surface_entity = hydra.Entity(name)
     surface_entity.create_entity(
-        center_point, 
+        center_point,
         ["Box Shape", "Shape Surface Tag Emitter"]
         )
     if surface_entity.id.IsValid():
@@ -56,7 +56,7 @@ def create_vegetation_area(name, center_point, box_size_x, box_size_y, box_size_
     # Create a vegetation area entity to use as our test vegetation spawner
     spawner_entity = hydra.Entity(name)
     spawner_entity.create_entity(
-        center_point, 
+        center_point,
         ["Vegetation Layer Spawner", "Box Shape", "Vegetation Asset List"]
         )
     if spawner_entity.id.IsValid():

+ 1 - 1
AutomatedTesting/Levels/Physics/Material_DefaultLibraryConsistentOnAllFeatures/cowboy.emfxworkspace

@@ -1,3 +1,3 @@
 [General]
 version=1
-startScript="ImportActor -filename \"@assets@/characters/cowboy/actor/cowboy_01.actor\"\nCreateActorInstance -actorID %LASTRESULT% -xPos 0.000000 -yPos 0.000000 -zPos 0.000000 -xScale 1.000000 -yScale 1.000000 -zScale 1.000000 -rot 0.00000000,0.00000000,0.00000000,1.00000000\n"
+startScript="ImportActor -filename \"@products@/characters/cowboy/actor/cowboy_01.actor\"\nCreateActorInstance -actorID %LASTRESULT% -xPos 0.000000 -yPos 0.000000 -zPos 0.000000 -xScale 1.000000 -yScale 1.000000 -zScale 1.000000 -rot 0.00000000,0.00000000,0.00000000,1.00000000\n"

+ 1 - 1
AutomatedTesting/Levels/Physics/Material_DefaultMaterialLibraryChangesWork/ws.emfxworkspace

@@ -1,3 +1,3 @@
 [General]
 version=1
-startScript="ImportActor -filename \"@assets@/levels/physics/c15096734_physxmaterials_defaultmateriallibrary/rin_skeleton_newgeo.actor\"\nCreateActorInstance -actorID %LASTRESULT% -xPos 0.000000 -yPos 0.000000 -zPos 0.000000 -xScale 1.000000 -yScale 1.000000 -zScale 1.000000 -rot 0.00000000,0.00000000,0.00000000,1.00000000\nLoadMotionSet -filename \"@assets@/Levels/Physics/C15096734_PhysxMaterials_DefaultMaterialLibrary/custom_motionset.motionset\"\nLoadAnimGraph -filename \"@assets@/Levels/Physics/C15096734_PhysxMaterials_DefaultMaterialLibrary/rin_physics.animgraph\"\n"
+startScript="ImportActor -filename \"@products@/levels/physics/c15096734_physxmaterials_defaultmateriallibrary/rin_skeleton_newgeo.actor\"\nCreateActorInstance -actorID %LASTRESULT% -xPos 0.000000 -yPos 0.000000 -zPos 0.000000 -xScale 1.000000 -yScale 1.000000 -zScale 1.000000 -rot 0.00000000,0.00000000,0.00000000,1.00000000\nLoadMotionSet -filename \"@products@/levels/physics/c15096734_physxmaterials_defaultmateriallibrary/custom_motionset.motionset\"\nLoadAnimGraph -filename \"@products@/levels/physics/c15096734_physxmaterials_defaultmateriallibrary/rin_physics.animgraph\"\n"

+ 6 - 7
Code/Editor/Controls/FolderTreeCtrl.cpp

@@ -278,17 +278,16 @@ void CFolderTreeCtrl::LoadTreeRec(const QString& currentFolder)
 
 void CFolderTreeCtrl::AddItem(const QString& path)
 {
-    QString folder;
-    QString fileNameWithoutExtension;
-    QString ext;
-
-    Path::Split(path, folder, fileNameWithoutExtension, ext);
+    AZ::IO::FixedMaxPath folder{ AZ::IO::PathView(path.toUtf8().constData()) };
+    AZ::IO::FixedMaxPath fileNameWithoutExtension = folder.Extension();
+    folder = folder.ParentPath();
 
     auto regex = QRegExp(m_fileNameSpec, Qt::CaseInsensitive, QRegExp::Wildcard);
     if (regex.exactMatch(path))
     {
-        CTreeItem* folderTreeItem = CreateFolderItems(folder);
-        folderTreeItem->AddChild(fileNameWithoutExtension, path, eTreeImage_File);
+        CTreeItem* folderTreeItem = CreateFolderItems(QString::fromUtf8(folder.c_str(), static_cast<int>(folder.Native().size())));
+        folderTreeItem->AddChild(QString::fromUtf8(fileNameWithoutExtension.c_str(),
+            static_cast<int>(fileNameWithoutExtension.Native().size())), path, eTreeImage_File);
     }
 }
 

+ 2 - 0
Code/Editor/Core/Tests/test_Main.cpp

@@ -33,6 +33,7 @@ public:
 protected:
     void SetupEnvironment() override
     {
+        AttachEditorCoreAZEnvironment(AZ::Environment::GetInstance());
         m_allocatorScope.ActivateAllocators();
         m_cryPak = new NiceMock<CryPakMock>();
 
@@ -49,6 +50,7 @@ protected:
     {
         delete m_cryPak;
         m_allocatorScope.DeactivateAllocators();
+        DetachEditorCoreAZEnvironment();
     }
 
 private:

+ 22 - 15
Code/Editor/Core/Tests/test_PathUtil.cpp

@@ -5,22 +5,29 @@
  * SPDX-License-Identifier: Apache-2.0 OR MIT
  *
  */
-#include "EditorDefs.h"
-#include <AzTest/AzTest.h>
-#include "Util/PathUtil.h"
-#include <CrySystemBus.h>
-
-TEST(PathUtil, GamePathToFullPath_DoesNotBufferOverflow)
+#include <AzCore/UnitTest/TestTypes.h>
+#include <Util/PathUtil.h>
+namespace UnitTest
 {
-    // There are no test assertions in this test because the purpose is just to verify that the test runs without crashing
-    QString pngExtension(".png");
+    class PathUtil
+        : public ScopedAllocatorSetupFixture
+    {
+    };
+
+    TEST_F(PathUtil, GamePathToFullPath_DoesNotBufferOverflow)
+    {
+        // There are no test assertions in this test because the purpose is just to verify that the test runs without crashing
+        QString pngExtension(".png");
 
-    // Create a string of lenth AZ_MAX_PATH_LEN that ends in .png
-    QString longStringMaxPath(AZ_MAX_PATH_LEN, 'x');
-    longStringMaxPath.replace(longStringMaxPath.length() - pngExtension.length(), longStringMaxPath.length(), pngExtension);
-    Path::GamePathToFullPath(longStringMaxPath);
+        // Create a string of length AZ_MAX_PATH_LEN that ends in .png
+        QString longStringMaxPath(AZ_MAX_PATH_LEN, 'x');
+        longStringMaxPath.replace(longStringMaxPath.length() - pngExtension.length(), longStringMaxPath.length(), pngExtension);
+        AZ_TEST_START_TRACE_SUPPRESSION;
+        Path::GamePathToFullPath(longStringMaxPath);
+        AZ_TEST_STOP_TRACE_SUPPRESSION_NO_COUNT;
 
-    QString longStringMaxPathPlusOne(AZ_MAX_PATH_LEN + 1, 'x');
-    longStringMaxPathPlusOne.replace(longStringMaxPathPlusOne.length() - pngExtension.length(), longStringMaxPathPlusOne.length(), pngExtension);
-    Path::GamePathToFullPath(longStringMaxPathPlusOne);
+        QString longStringMaxPathPlusOne(AZ_MAX_PATH_LEN + 1, 'x');
+        longStringMaxPathPlusOne.replace(longStringMaxPathPlusOne.length() - pngExtension.length(), longStringMaxPathPlusOne.length(), pngExtension);
+        Path::GamePathToFullPath(longStringMaxPathPlusOne);
+    }
 }

+ 1 - 1
Code/Editor/CryEdit.cpp

@@ -2642,7 +2642,7 @@ void CCryEditApp::OnFileResaveSlices()
     sliceAssetInfos.reserve(5000);
     AZ::Data::AssetCatalogRequests::AssetEnumerationCB sliceCountCb = [&sliceAssetInfos]([[maybe_unused]] const AZ::Data::AssetId id, const AZ::Data::AssetInfo& info)
     {
-        // Only add slices and nothing that has been temporarily added to the catalog with a macro in it (ie @devroot@)
+        // Only add slices and nothing that has been temporarily added to the catalog with a macro in it (ie @engroot@)
         if (info.m_assetType == azrtti_typeid<AZ::SliceAsset>() && info.m_relativePath[0] != '@')
         {
             sliceAssetInfos.push_back(info);

+ 2 - 2
Code/Editor/CryEditDoc.cpp

@@ -1108,7 +1108,7 @@ bool CCryEditDoc::SaveLevel(const QString& filename)
     if (QFileInfo(filename).isRelative())
     {
         // Resolving the path through resolvepath would normalize and lowcase it, and in this case, we don't want that.
-        fullPathName = Path::ToUnixPath(QDir(QString::fromUtf8(gEnv->pFileIO->GetAlias("@devassets@"))).absoluteFilePath(fullPathName));
+        fullPathName = Path::ToUnixPath(QDir(QString::fromUtf8(gEnv->pFileIO->GetAlias("@projectroot@"))).absoluteFilePath(fullPathName));
     }
 
     if (!CFileUtil::OverwriteFile(fullPathName))
@@ -2159,7 +2159,7 @@ bool CCryEditDoc::LoadXmlArchiveArray(TDocMultiArchive& arrXmlAr, const QString&
         xmlAr.bLoading = true;
 
         // bound to the level folder, as if it were the assets folder.
-        // this mounts (whateverlevelname.ly) as @assets@/Levels/whateverlevelname/ and thus it works...
+        // this mounts (whateverlevelname.ly) as @products@/Levels/whateverlevelname/ and thus it works...
         bool openLevelPakFileSuccess = pIPak->OpenPack(levelPath.toUtf8().data(), absoluteLevelPath.toUtf8().data());
         if (!openLevelPakFileSuccess)
         {

+ 1 - 1
Code/Editor/Dialogs/PythonScriptsDialog.cpp

@@ -91,7 +91,7 @@ CPythonScriptsDialog::CPythonScriptsDialog(QWidget* parent)
             {
                 AZ::IO::Path newSourcePath = jsonSourcePathPointer;
                 // Resolve any file aliases first - Do not use ResolvePath() as that assumes
-                // any relative path is underneath the @assets@ alias
+                // any relative path is underneath the @products@ alias
                 if (auto fileIoBase = AZ::IO::FileIOBase::GetInstance(); fileIoBase != nullptr)
                 {
                     AZ::IO::FixedMaxPath replacedAliasPath;

+ 11 - 25
Code/Editor/EditorFileMonitor.cpp

@@ -14,6 +14,8 @@
 // Editor
 #include "CryEdit.h"
 
+#include <AzCore/Utils/Utils.h>
+
 //////////////////////////////////////////////////////////////////////////
 CEditorFileMonitor::CEditorFileMonitor()
 {
@@ -177,26 +179,14 @@ void CEditorFileMonitor::OnFileMonitorChange(const SFileChangeInfo& rChange)
     // Make file relative to PrimaryCD folder.
     QString filename = rChange.filename;
 
-    // Remove game directory if present in path.
-    const QString rootPath =
-        QDir::fromNativeSeparators(QString::fromLatin1(Path::GetEditingRootFolder().c_str()));
-    if (filename.startsWith(rootPath, Qt::CaseInsensitive))
-    {
-        filename = filename.right(filename.length() - rootPath.length());
-    }
-
-    // Make sure there is no leading slash
-    if (!filename.isEmpty() && (filename[0] == '\\' || filename[0] == '/'))
-    {
-        filename = filename.mid(1);
-    }
+    // Make path relative to the the project directory
+    AZ::IO::Path projectPath{ AZ::Utils::GetProjectPath() };
+    AZ::IO::FixedMaxPath projectRelativeFilePath = AZ::IO::PathView(filename.toUtf8().constData()).LexicallyProximate(
+        projectPath);
 
-    if (!filename.isEmpty())
+    if (!projectRelativeFilePath.empty())
     {
-        //remove game name. Make it relative to the game folder
-        const QString filenameRelGame = RemoveGameName(filename);
-        const int extIndex = filename.lastIndexOf('.');
-        const QString ext = filename.right(filename.length() - 1 - extIndex);
+        AZ::IO::PathView ext = projectRelativeFilePath.Extension();
 
         // Check for File Monitor callback
         std::vector<SFileChangeCallback>::iterator iter;
@@ -207,15 +197,11 @@ void CEditorFileMonitor::OnFileMonitorChange(const SFileChangeInfo& rChange)
             // We compare against length of callback string, so we get directory matches as well as full filenames
             if (sCallback.pListener)
             {
-                if (sCallback.extension == "*" || ext.compare(sCallback.extension, Qt::CaseInsensitive) == 0)
+                if (sCallback.extension == "*" || AZ::IO::PathView(sCallback.extension.toUtf8().constData()) == ext)
                 {
-                    if (filenameRelGame.compare(sCallback.item, Qt::CaseInsensitive) == 0)
-                    {
-                        sCallback.pListener->OnFileChange(qPrintable(filenameRelGame), IFileChangeListener::EChangeType(rChange.changeType));
-                    }
-                    else if (filename.compare(sCallback.item, Qt::CaseInsensitive) == 0)
+                    if (AZ::IO::PathView(sCallback.item.toUtf8().constData()) == projectRelativeFilePath)
                     {
-                        sCallback.pListener->OnFileChange(qPrintable(filename), IFileChangeListener::EChangeType(rChange.changeType));
+                        sCallback.pListener->OnFileChange(qPrintable(projectRelativeFilePath.c_str()), IFileChangeListener::EChangeType(rChange.changeType));
                     }
                 }
             }

+ 1 - 1
Code/Editor/Plugins/ComponentEntityEditorPlugin/SandboxIntegration.cpp

@@ -1895,7 +1895,7 @@ void SandboxIntegrationManager::MakeSliceFromEntities(const AzToolsFramework::En
     AzToolsFramework::ToolsApplicationRequestBus::BroadcastResult(entitiesAndDescendants,
         &AzToolsFramework::ToolsApplicationRequestBus::Events::GatherEntitiesAndAllDescendents, entities);
 
-    const AZStd::string slicesAssetsPath = "@devassets@/Slices";
+    const AZStd::string slicesAssetsPath = "@projectroot@/Slices";
 
     if (!gEnv->pFileIO->Exists(slicesAssetsPath.c_str()))
     {

+ 24 - 55
Code/Editor/Plugins/EditorAssetImporter/AssetImporterWindow.cpp

@@ -30,6 +30,7 @@ class CXTPDockingPaneLayout; // Needed for settings.h
 #include <AzCore/Component/ComponentApplicationBus.h>
 #include <AzCore/IO/Path/Path.h>
 #include <AzCore/std/functional.h>
+#include <AzCore/Utils/Utils.h>
 #include <AzFramework/StringFunc/StringFunc.h>
 #include <AzCore/std/string/conversions.h>
 #include <Util/PathUtil.h>
@@ -47,41 +48,6 @@ class CXTPDockingPaneLayout; // Needed for settings.h
 const char* AssetImporterWindow::s_documentationWebAddress = "http://docs.aws.amazon.com/lumberyard/latest/userguide/char-fbx-importer.html";
 const AZ::Uuid AssetImporterWindow::s_browseTag = AZ::Uuid::CreateString("{C240D2E1-BFD2-4FFA-BB5B-CC0FA389A5D3}");
 
-void MakeUserFriendlySourceAssetPath(QString& out, const QString& sourcePath)
-{
-    char devAssetsRoot[AZ_MAX_PATH_LEN] = { 0 };
-    if (!gEnv->pFileIO->ResolvePath("@devroot@", devAssetsRoot, AZ_MAX_PATH_LEN))
-    {
-        out = sourcePath;
-        return;
-    }
-
-    AZStd::replace(devAssetsRoot, devAssetsRoot + AZ_MAX_PATH_LEN- 1, AZ_WRONG_FILESYSTEM_SEPARATOR, AZ_CORRECT_FILESYSTEM_SEPARATOR);
- 
-    // Find if the sourcePathArray is a sub directory of the devAssets folder 
-    // Keep reference to sourcePathArray long enough to use in PathView
-    QByteArray sourcePathArray = sourcePath.toUtf8();
-    AZ::IO::PathView sourcePathRootView(sourcePathArray.data());
-    AZ::IO::PathView devAssetsRootView(devAssetsRoot);
-    auto [sourcePathIter, devAssetsIter] = AZStd::mismatch(sourcePathRootView.begin(), sourcePathRootView.end(),
-        devAssetsRootView.begin(), devAssetsRootView.end());
-    // If the devAssets path iterator is not equal to the end, then there was a mismistch while comparing it
-    // against the source path indicating that the source path is not a sub-directory
-    if (devAssetsIter != devAssetsRootView.end())
-    {
-        out = sourcePath;
-        return;
-    }
-
-    int offset = aznumeric_cast<int>(strlen(devAssetsRoot));
-    if (sourcePath.at(offset) == AZ_CORRECT_FILESYSTEM_SEPARATOR)
-    {
-        ++offset;
-    }
-    out = sourcePath.right(sourcePath.length() - offset);
-
-}
-
 AssetImporterWindow::AssetImporterWindow()
     : AssetImporterWindow(nullptr)
 {
@@ -102,7 +68,7 @@ AssetImporterWindow::AssetImporterWindow(QWidget* parent)
 
 AssetImporterWindow::~AssetImporterWindow()
 {
-    AZ_Assert(m_processingOverlayIndex == AZ::SceneAPI::UI::OverlayWidget::s_invalidOverlayIndex, 
+    AZ_Assert(m_processingOverlayIndex == AZ::SceneAPI::UI::OverlayWidget::s_invalidOverlayIndex,
         "Processing overlay (and potentially background thread) still active at destruction.");
     AZ_Assert(!m_processingOverlay, "Processing overlay (and potentially background thread) still active at destruction.");
 }
@@ -133,7 +99,7 @@ void AssetImporterWindow::OpenFile(const AZStd::string& filePath)
         QMessageBox::warning(this, "In progress", "Unable to close one or more windows at this time.");
         return;
     }
-    
+
     OpenFileInternal(filePath);
 }
 
@@ -146,7 +112,7 @@ void AssetImporterWindow::closeEvent(QCloseEvent* ev)
 
     if (m_processingOverlay)
     {
-        AZ_Assert(m_processingOverlayIndex != AZ::SceneAPI::UI::OverlayWidget::s_invalidOverlayIndex, 
+        AZ_Assert(m_processingOverlayIndex != AZ::SceneAPI::UI::OverlayWidget::s_invalidOverlayIndex,
             "Processing overlay present, but not the index in the overlay for it.");
         if (m_processingOverlay->HasProcessingCompleted())
         {
@@ -157,7 +123,7 @@ void AssetImporterWindow::closeEvent(QCloseEvent* ev)
             }
             else
             {
-                QMessageBox::critical(this, "Processing In Progress", "Unable to close the result window at this time.", 
+                QMessageBox::critical(this, "Processing In Progress", "Unable to close the result window at this time.",
                     QMessageBox::Ok, QMessageBox::Ok);
                 ev->ignore();
                 return;
@@ -165,7 +131,7 @@ void AssetImporterWindow::closeEvent(QCloseEvent* ev)
         }
         else
         {
-            QMessageBox::critical(this, "Processing In Progress", "Please wait until processing has completed to try again.", 
+            QMessageBox::critical(this, "Processing In Progress", "Please wait until processing has completed to try again.",
                 QMessageBox::Ok, QMessageBox::Ok);
             ev->ignore();
             return;
@@ -199,7 +165,9 @@ void AssetImporterWindow::Init()
     // Load the style sheets
     AzQtComponents::StylesheetPreprocessor styleSheetProcessor(nullptr);
 
-    AZStd::string mainWindowQSSPath = Path::GetEditingRootFolder() + "\\Editor\\Styles\\AssetImporterWindow.qss";
+    auto mainWindowQSSPath = AZ::IO::Path(AZ::Utils::GetEnginePath()) / "Assets";
+    mainWindowQSSPath /= "Editor/Styles/AssetImporterWindow.qss";
+    mainWindowQSSPath.MakePreferred();
     QFile mainWindowStyleSheetFile(mainWindowQSSPath.c_str());
     if (mainWindowStyleSheetFile.open(QFile::ReadOnly))
     {
@@ -212,7 +180,7 @@ void AssetImporterWindow::Init()
     {
         ui->m_actionInspect->setVisible(false);
     }
-    
+
     ResetMenuAccess(WindowState::InitialNothingLoaded);
 
     // Setup the overlay system, and set the root to be the root display. The root display has the browse,
@@ -220,7 +188,7 @@ void AssetImporterWindow::Init()
     m_overlay.reset(aznew AZ::SceneAPI::UI::OverlayWidget(this));
     m_rootDisplay.reset(aznew ImporterRootDisplay(m_serializeContext));
     connect(m_rootDisplay.data(), &ImporterRootDisplay::UpdateClicked, this, &AssetImporterWindow::UpdateClicked);
-    
+
     connect(m_overlay.data(), &AZ::SceneAPI::UI::OverlayWidget::LayerAdded, this, &AssetImporterWindow::OverlayLayerAdded);
     connect(m_overlay.data(), &AZ::SceneAPI::UI::OverlayWidget::LayerRemoved, this, &AssetImporterWindow::OverlayLayerRemoved);
 
@@ -242,7 +210,7 @@ void AssetImporterWindow::Init()
         AZStd::string joinedExtensions;
         AzFramework::StringFunc::Join(joinedExtensions, extensions.begin(), extensions.end(), " or ");
 
-        AZStd::string firstLineText = 
+        AZStd::string firstLineText =
             AZStd::string::format(
                 "%s files are available for use after placing them in any folder within your game project. "
                 "These files will automatically be processed and may be accessed via the Asset Browser. <a href=\"%s\">Learn more...</a>",
@@ -250,13 +218,13 @@ void AssetImporterWindow::Init()
 
         ui->m_initialPromptFirstLine->setText(firstLineText.c_str());
 
-        AZStd::string secondLineText = 
+        AZStd::string secondLineText =
             AZStd::string::format("To adjust the %s settings, right-click the file in the Asset Browser and select \"Edit Settings\" from the context menu.", joinedExtensions.c_str());
         ui->m_initialPromptSecondLine->setText(secondLineText.c_str());
     }
     else
     {
-        AZStd::string firstLineText = 
+        AZStd::string firstLineText =
             AZStd::string::format(
                 "Files are available for use after placing them in any folder within your game project. "
                 "These files will automatically be processed and may be accessed via the Asset Browser. <a href=\"%s\">Learn more...</a>", s_documentationWebAddress);
@@ -282,12 +250,12 @@ void AssetImporterWindow::OpenFileInternal(const AZStd::string& filePath)
     auto asyncLoadHandler = AZStd::make_shared<AZ::SceneAPI::SceneUI::AsyncOperationProcessingHandler>(
         s_browseTag,
         [this, filePath]()
-        { 
-            m_assetImporterDocument->LoadScene(filePath); 
+        {
+            m_assetImporterDocument->LoadScene(filePath);
         },
         [this]()
         {
-            HandleAssetLoadingCompleted(); 
+            HandleAssetLoadingCompleted();
         }, this);
 
     m_processingOverlay.reset(new ProcessingOverlayWidget(m_overlay.data(), ProcessingOverlayWidget::Layout::Loading, s_browseTag));
@@ -304,7 +272,7 @@ bool AssetImporterWindow::IsAllowedToChangeSourceFile()
         return true;
     }
 
-    QMessageBox messageBox(QMessageBox::Icon::NoIcon, "Unsaved changes", 
+    QMessageBox messageBox(QMessageBox::Icon::NoIcon, "Unsaved changes",
         "You have unsaved changes. Do you want to discard those changes?",
         QMessageBox::StandardButton::Discard | QMessageBox::StandardButton::Cancel, this);
     messageBox.exec();
@@ -406,7 +374,7 @@ void AssetImporterWindow::OnSceneResetRequested()
             else
             {
                 m_assetImporterDocument->ClearScene();
-                AZ_TracePrintf(ErrorWindow, "Manifest reset returned in '%s'", 
+                AZ_TracePrintf(ErrorWindow, "Manifest reset returned in '%s'",
                     result.GetResult() == ProcessingResult::Failure ? "Failure" : "Ignored");
             }
         },
@@ -456,7 +424,7 @@ void AssetImporterWindow::OnInspect()
     // make sure the inspector doesn't outlive the AssetImporterWindow, since we own the data it will be inspecting.
     auto* theInspectWidget = aznew AZ::SceneAPI::UI::SceneGraphInspectWidget(*m_assetImporterDocument->GetScene());
     QObject::connect(this, &QObject::destroyed, theInspectWidget, [theInspectWidget]() { theInspectWidget->window()->close(); } );
-    
+
     m_overlay->PushLayer(label, theInspectWidget, "Scene Inspector", buttons);
 }
 
@@ -483,7 +451,7 @@ void AssetImporterWindow::OverlayLayerRemoved()
     else
     {
         ResetMenuAccess(WindowState::InitialNothingLoaded);
-        
+
         ui->m_initialBrowseContainer->show();
         m_rootDisplay->hide();
     }
@@ -533,8 +501,9 @@ void AssetImporterWindow::HandleAssetLoadingCompleted()
     m_fullSourcePath = m_assetImporterDocument->GetScene()->GetSourceFilename();
     SetTitle(m_fullSourcePath.c_str());
 
-    QString userFriendlyFileName;
-    MakeUserFriendlySourceAssetPath(userFriendlyFileName, m_fullSourcePath.c_str());
+    AZ::IO::FixedMaxPath projectPath = AZ::Utils::GetProjectPath();
+    AZ::IO::FixedMaxPath relativeSourcePath = AZ::IO::PathView(m_fullSourcePath).LexicallyProximate(projectPath);
+    auto userFriendlyFileName = QString::fromUtf8(relativeSourcePath.c_str(), static_cast<int>(relativeSourcePath.Native().size()));
     m_rootDisplay->SetSceneDisplay(userFriendlyFileName, m_assetImporterDocument->GetScene());
 
     // Once we've browsed to something successfully, we need to hide the initial browse button layer and

+ 10 - 15
Code/Editor/Plugins/EditorAssetImporter/SceneSerializationHandler.cpp

@@ -7,9 +7,11 @@
  */
 
 #include <AzCore/Debug/Profiler.h>
+#include <AzCore/IO/Path/Path.h>
 #include <AzCore/IO/SystemFile.h>
 #include <AzCore/std/algorithm.h>
 #include <AzCore/std/string/conversions.h>
+#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
 #include <AzFramework/StringFunc/StringFunc.h>
 #include <AzToolsFramework/API/EditorAssetSystemAPI.h>
 #include <AzToolsFramework/Debug/TraceContext.h>
@@ -50,22 +52,15 @@ namespace AZ
             return nullptr;
         }
 
-        AZStd::string cleanPath = filePath;
-        if (AzFramework::StringFunc::Path::IsRelative(filePath.c_str()))
+        AZ::IO::Path enginePath;
+        if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
         {
-            const char* absolutePath = nullptr;
-            AzToolsFramework::AssetSystemRequestBus::BroadcastResult(absolutePath, 
-                &AzToolsFramework::AssetSystemRequestBus::Events::GetAbsoluteDevRootFolderPath);
-            AZ_Assert(absolutePath, "Unable to retrieve the dev folder path");
-            AzFramework::StringFunc::Path::Join(absolutePath, cleanPath.c_str(), cleanPath);
-        }
-        else
-        {
-            // Normalizing is not needed if the path is relative as Join(...) will also normalize.
-            AzFramework::StringFunc::Path::Normalize(cleanPath);
+            settingsRegistry->Get(enginePath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder);
         }
 
-        auto sceneIt = m_scenes.find(cleanPath);
+        AZ::IO::Path cleanPath = (enginePath / filePath).LexicallyNormal();
+
+        auto sceneIt = m_scenes.find(cleanPath.Native());
         if (sceneIt != m_scenes.end())
         {
             AZStd::shared_ptr<SceneAPI::Containers::Scene> scene = sceneIt->second.lock();
@@ -98,14 +93,14 @@ namespace AZ
         }
 
         AZStd::shared_ptr<SceneAPI::Containers::Scene> scene = 
-            AssetImportRequest::LoadSceneFromVerifiedPath(cleanPath, sceneSourceGuid, AssetImportRequest::RequestingApplication::Editor, SceneAPI::SceneCore::LoadingComponent::TYPEINFO_Uuid());
+            AssetImportRequest::LoadSceneFromVerifiedPath(cleanPath.Native(), sceneSourceGuid, AssetImportRequest::RequestingApplication::Editor, SceneAPI::SceneCore::LoadingComponent::TYPEINFO_Uuid());
         if (!scene)
         {
             AZ_TracePrintf(Utilities::ErrorWindow, "Failed to load the requested scene.");
             return nullptr;
         }
 
-        m_scenes.emplace(AZStd::move(cleanPath), scene);
+        m_scenes.emplace(AZStd::move(cleanPath.Native()), scene);
         
         return scene;
     }

+ 0 - 1
Code/Editor/Plugins/ProjectSettingsTool/ProjectSettingsToolWindow.cpp

@@ -46,7 +46,6 @@ namespace ProjectSettingsTool
         , LastPathBus::Handler()
         , m_ui(new Ui::ProjectSettingsToolWidget())
         , m_reconfigureProcess()
-        , m_devRoot(GetDevRoot())
         , m_projectRoot(GetProjectRoot())
         , m_projectName(GetProjectName())
         , m_plistsInitVector(

+ 0 - 1
Code/Editor/Plugins/ProjectSettingsTool/ProjectSettingsToolWindow.h

@@ -147,7 +147,6 @@ namespace ProjectSettingsTool
         // The process used to reconfigure settings
         QProcess m_reconfigureProcess;
 
-        AZStd::string m_devRoot;
         AZStd::string m_projectRoot;
         AZStd::string m_projectName;
 

+ 14 - 20
Code/Editor/Plugins/ProjectSettingsTool/Utils.cpp

@@ -27,37 +27,31 @@ namespace
     }
 
     template<typename StringType>
-    StringType GetAbsoluteDevRoot()
+    StringType GetAbsoluteEngineRoot()
     {
-        const char* devRoot = nullptr;
-        AzToolsFramework::AssetSystemRequestBus::BroadcastResult(
-            devRoot,
-            &AzToolsFramework::AssetSystemRequestBus::Handler::GetAbsoluteDevRootFolderPath);
+        AZ::IO::FixedMaxPath engineRoot = AZ::Utils::GetEnginePath();
 
-        if (!devRoot)
+        if (engineRoot.empty())
         {
             return "";
         }
 
-        StringType devRootString(devRoot);
-        ToUnixPath(devRootString);
-        return devRootString;
+        StringType engineRootString(engineRoot.c_str());
+        ToUnixPath(engineRootString);
+        return engineRootString;
     }
 
     template<typename StringType>
     StringType GetAbsoluteProjectRoot()
     {
-        const char* projectRoot = nullptr;
-        AzToolsFramework::AssetSystemRequestBus::BroadcastResult(
-            projectRoot,
-            &AzToolsFramework::AssetSystemRequestBus::Handler::GetAbsoluteDevGameFolderPath);
+        AZ::IO::FixedMaxPath projectRoot = AZ::Utils::GetProjectPath();
 
-        if (!projectRoot)
+        if (projectRoot.empty())
         {
             return "";
         }
 
-        StringType projectRootString(projectRoot);
+        StringType projectRootString(projectRoot.c_str());
         ToUnixPath(projectRootString);
         return projectRootString;
     }
@@ -87,9 +81,9 @@ namespace ProjectSettingsTool
         return reinterpret_cast<void*>(func);
     }
 
-    AZStd::string GetDevRoot()
+    AZStd::string GetEngineRoot()
     {
-        return GetAbsoluteDevRoot<AZStd::string>();
+        return GetAbsoluteEngineRoot<AZStd::string>();
     }
     AZStd::string GetProjectRoot()
     {
@@ -104,7 +98,7 @@ namespace ProjectSettingsTool
     QString SelectXmlFromFileDialog(const QString& currentFile)
     {
         // The selected file must be relative to this path
-        QString defaultPath = GetAbsoluteDevRoot<QString>();
+        QString defaultPath = GetAbsoluteEngineRoot<QString>();
         QString startPath;
 
         // Choose the starting path for file dialog
@@ -139,7 +133,7 @@ namespace ProjectSettingsTool
 
     QString SelectImageFromFileDialog(const QString& currentFile)
     {
-        QString defaultPath = QStringLiteral("%1Code%2/Resources/").arg(GetAbsoluteDevRoot<QString>(), ::GetProjectName<QString>());
+        QString defaultPath = QStringLiteral("%1Code%2/Resources/").arg(GetAbsoluteEngineRoot<QString>(), ::GetProjectName<QString>());
 
         QString startPath;
 
@@ -188,7 +182,7 @@ namespace ProjectSettingsTool
         // Android
         if (group <= ImageGroup::AndroidPortrait)
         {
-            root = GetDevRoot() + "/Code/Tools/Android/ProjectBuilder/app_";
+            root = GetEngineRoot() + "/Code/Tools/Android/ProjectBuilder/app_";
         }
         //Ios
         else

+ 1 - 1
Code/Editor/Plugins/ProjectSettingsTool/Utils.h

@@ -17,7 +17,7 @@
 namespace ProjectSettingsTool
 {
     void* ConvertFunctorToVoid(AZStd::pair<QValidator::State, const QString>(*func)(const QString&));
-    AZStd::string GetDevRoot();
+    AZStd::string GetEngineRoot();
     AZStd::string GetProjectRoot();
     AZStd::string GetProjectName();
 

+ 3 - 2
Code/Editor/Settings.cpp

@@ -935,8 +935,9 @@ void SEditorSettings::LoadDefaultGamePaths()
         searchPaths[EDITOR_PATH_MATERIALS].push_back((Path::GetEditingGameDataFolder() + "/Materials").c_str());
     }
 
-    AZStd::string iconsPath;
-    AZ::StringFunc::Path::Join(Path::GetEditingRootFolder().c_str(), "Editor/UI/Icons", iconsPath);
+    auto iconsPath = AZ::IO::Path(AZ::Utils::GetEnginePath()) / "Assets";
+    iconsPath /= "Editor/UI/Icons";
+    iconsPath.MakePreferred();
     searchPaths[EDITOR_PATH_UI_ICONS].push_back(iconsPath.c_str());
 }
 

+ 13 - 13
Code/Editor/TrackView/SequenceBatchRenderDialog.cpp

@@ -269,7 +269,7 @@ void CSequenceBatchRenderDialog::OnRenderItemSelChange()
     // Enable/disable the 'remove'/'update' button properly.
     bool bNoSelection = !m_ui->m_renderList->selectionModel()->hasSelection();
     m_ui->BATCH_RENDER_REMOVE_SEQ->setEnabled(bNoSelection ? false : true);
-    
+
     CheckForEnableUpdateButton();
 
     if (bNoSelection)
@@ -360,7 +360,7 @@ void CSequenceBatchRenderDialog::OnRenderItemSelChange()
         cvarsText += item.cvars[static_cast<int>(i)];
         cvarsText += "\r\n";
     }
-    m_ui->m_cvarsEdit->setPlainText(cvarsText);    
+    m_ui->m_cvarsEdit->setPlainText(cvarsText);
 }
 
 void CSequenceBatchRenderDialog::CheckForEnableUpdateButton()
@@ -494,7 +494,7 @@ void CSequenceBatchRenderDialog::OnSavePreset()
 }
 
 void CSequenceBatchRenderDialog::stashActiveViewportResolution()
-{   
+{
     // stash active resolution in global vars
     activeViewportWidth = resolutions[0][0];
     activeViewportHeight = resolutions[0][1];
@@ -502,7 +502,7 @@ void CSequenceBatchRenderDialog::stashActiveViewportResolution()
     if (activeViewport)
     {
         activeViewport->GetDimensions(&activeViewportWidth, &activeViewportHeight);
-    }  
+    }
 }
 
 void CSequenceBatchRenderDialog::OnGo()
@@ -640,7 +640,7 @@ void CSequenceBatchRenderDialog::OnResolutionSelected()
         int defaultH;
         const QString currentCustomResText = m_ui->m_resolutionCombo->currentText();
         GetResolutionFromCustomResText(currentCustomResText.toStdString().c_str(), defaultW, defaultH);
-        
+
         CCustomResolutionDlg resDlg(defaultW, defaultH, this);
         if (resDlg.exec() == QDialog::Accepted)
         {
@@ -752,7 +752,7 @@ bool CSequenceBatchRenderDialog::LoadOutputOptions(const QString& pathname)
         {
             const QString customResText = resolutionNode->getContent();
             m_ui->m_resolutionCombo->setItemText(curSel, customResText);
-            
+
             GetResolutionFromCustomResText(customResText.toStdString().c_str(), m_customResW, m_customResH);
         }
         m_ui->m_resolutionCombo->setCurrentIndex(curSel);
@@ -907,12 +907,12 @@ void CSequenceBatchRenderDialog::CaptureItemStart()
     folder += "/";
     folder += itemText;
 
-    // If this is a relative path, prepend the @assets@ folder to match where the Renderer is going
+    // If this is a relative path, prepend the @products@ folder to match where the Renderer is going
     // to dump the frame buffer image captures.
     if (AzFramework::StringFunc::Path::IsRelative(folder.toUtf8().data()))
     {
         AZStd::string absolutePath;
-        AZStd::string assetsRoot = AZ::IO::FileIOBase::GetInstance()->GetAlias("@assets@");
+        AZStd::string assetsRoot = AZ::IO::FileIOBase::GetInstance()->GetAlias("@products@");
         AzFramework::StringFunc::Path::Join(assetsRoot.c_str(), folder.toUtf8().data(), absolutePath);
         folder = absolutePath.c_str();
     }
@@ -962,7 +962,7 @@ void CSequenceBatchRenderDialog::CaptureItemStart()
         m_renderContext.cvarDisplayInfoBU = cvarDebugInfo->GetIVal();
         if (renderItem.disableDebugInfo && cvarDebugInfo->GetIVal())
         {
-            const int DISPLAY_INFO_OFF = 0;         
+            const int DISPLAY_INFO_OFF = 0;
             cvarDebugInfo->Set(DISPLAY_INFO_OFF);
         }
     }
@@ -1100,13 +1100,13 @@ void CSequenceBatchRenderDialog::OnUpdateEnd(IAnimSequence* sequence)
     sequence->SetActiveDirector(m_renderContext.pActiveDirectorBU);
 
     const auto imageFormat = m_ui->m_imageFormatCombo->currentText();
-    
+
     SRenderItem renderItem = m_renderItems[m_renderContext.currentItemIndex];
     if (m_bFFMPEGCommandAvailable && renderItem.bCreateVideo)
     {
         // Create a video using the ffmpeg plug-in from captured images.
         m_renderContext.processingFFMPEG = true;
-       
+
         AZStd::string outputFolder = m_renderContext.captureOptions.folder;
         auto future = QtConcurrent::run(
             [renderItem, outputFolder, imageFormat]
@@ -1238,7 +1238,7 @@ void CSequenceBatchRenderDialog::OnKickIdleTimout()
 }
 
 void CSequenceBatchRenderDialog::OnKickIdle()
-{    
+{
     if (m_renderContext.captureState == CaptureState::WarmingUpAfterResChange)
     {
         OnUpdateWarmingUpAfterResChange();
@@ -1254,7 +1254,7 @@ void CSequenceBatchRenderDialog::OnKickIdle()
     else if (m_renderContext.captureState == CaptureState::Capturing)
     {
         OnUpdateCapturing();
-    }    
+    }
     else if (m_renderContext.captureState == CaptureState::End)
     {
         OnUpdateEnd(m_renderContext.endingSequence);

+ 33 - 167
Code/Editor/Util/PathUtil.cpp

@@ -11,9 +11,9 @@
 
 #include "PathUtil.h"
 
-#include <AzCore/IO/SystemFile.h> // for AZ_MAX_PATH_LEN
+#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
+#include <AzCore/Utils/Utils.h>
 #include <AzToolsFramework/API/EditorAssetSystemAPI.h> // for ebus events
-#include <AzFramework/StringFunc/StringFunc.h>
 #include <AzFramework/API/ApplicationAPI.h>
 #include <AzCore/std/string/conversions.h>
 #include <AzFramework/IO/LocalFileIO.h>
@@ -179,7 +179,7 @@ namespace Path
         EBUS_EVENT_RESULT(engineRoot, AzFramework::ApplicationRequests::Bus, GetEngineRoot);
         return QString(engineRoot);
     }
-    
+
     //////////////////////////////////////////////////////////////////////////
     QString& ReplaceFilename(const QString& strFilepath, const QString& strFilename, QString& strOutputFilename, bool bCallCaselessPath)
     {
@@ -216,30 +216,21 @@ namespace Path
     //////////////////////////////////////////////////////////////////////////
     QString GetResolvedUserSandboxFolder()
     {
-        char resolvedPath[AZ_MAX_PATH_LEN] = { 0 };
-        gEnv->pFileIO->ResolvePath(GetUserSandboxFolder().toUtf8().data(), resolvedPath, AZ_MAX_PATH_LEN);
-        return QString::fromLatin1(resolvedPath);
+        AZ::IO::FixedMaxPath userSandboxFolderPath;
+        gEnv->pFileIO->ResolvePath(userSandboxFolderPath, GetUserSandboxFolder().toUtf8().constData());
+        return QString::fromUtf8(userSandboxFolderPath.c_str(), static_cast<int>(userSandboxFolderPath.Native().size()));
     }
 
     // internal function, you should use GetEditingGameDataFolder instead.
     AZStd::string GetGameAssetsFolder()
     {
-        const char* resultValue = nullptr;
-        EBUS_EVENT_RESULT(resultValue, AzToolsFramework::AssetSystemRequestBus, GetAbsoluteDevGameFolderPath);
-        if (!resultValue)
-        {
-            if ((gEnv) && (gEnv->pFileIO))
-            {
-                resultValue = gEnv->pFileIO->GetAlias("@devassets@");
-            }
-        }
-
-        if (!resultValue)
+        AZ::IO::Path projectPath;
+        if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
         {
-            resultValue = ".";
+            settingsRegistry->Get(projectPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_ProjectPath);
         }
 
-        return resultValue;
+        return projectPath.Native();
     }
 
     /// Get the data folder
@@ -258,26 +249,6 @@ namespace Path
         return str;
     }
 
-    //! Get the root folder (in source control or other writable assets) where you should save root data.
-    AZStd::string GetEditingRootFolder()
-    {
-        const char* resultValue = nullptr;
-        EBUS_EVENT_RESULT(resultValue, AzToolsFramework::AssetSystemRequestBus, GetAbsoluteDevRootFolderPath);
-
-        if (!resultValue)
-        {
-            if ((gEnv) && (gEnv->pFileIO))
-            {
-                resultValue = gEnv->pFileIO->GetAlias("@devassets@");
-            }
-        }
-        if (!resultValue)
-        {
-            resultValue = ".";
-        }
-        return resultValue;
-    }
-
 
     AZStd::string MakeModPathFromGamePath(const char* relGamePath)
     {
@@ -335,165 +306,60 @@ namespace Path
             return "";
         }
 
-        bool relPathfound = false;
+        bool relPathFound = false;
         AZStd::string relativePath;
         AZStd::string fullAssetPath(fullPath.toUtf8().data());
-        EBUS_EVENT_RESULT(relPathfound, AzToolsFramework::AssetSystemRequestBus, GetRelativeProductPathFromFullSourceOrProductPath, fullAssetPath, relativePath);
+        EBUS_EVENT_RESULT(relPathFound, AzToolsFramework::AssetSystemRequestBus, GetRelativeProductPathFromFullSourceOrProductPath, fullAssetPath, relativePath);
 
-        if (relPathfound)
+        if (relPathFound)
         {
             // do not normalize this path, it will already be an appropriate asset ID.
             return CaselessPaths(relativePath.c_str());
         }
 
-        char rootpath[_MAX_PATH] = { 0 };
-        azstrcpy(rootpath, _MAX_PATH, Path::GetEditingRootFolder().c_str());
-
-        if (bRelativeToGameFolder)
-        {
-            azstrcpy(rootpath, _MAX_PATH, Path::GetEditingGameDataFolder().c_str());
-        }
-
-        QString rootPathNormalized(rootpath);
-        QString srcPathNormalized(fullPath);
-
-#if defined(AZ_PLATFORM_WINDOWS)
-        // avoid confusing PathRelativePathTo
-        rootPathNormalized.replace('/', '\\');
-        srcPathNormalized.replace('/', '\\');
-#endif
+        AZ::IO::FixedMaxPath rootPath = bRelativeToGameFolder ? AZ::Utils::GetProjectPath() : AZ::Utils::GetEnginePath();
+        AZ::IO::FixedMaxPath resolvedFullPath;
+        AZ::IO::FileIOBase::GetDirectInstance()->ResolvePath(resolvedFullPath, fullPath.toUtf8().constData());
 
         // Create relative path
-        char resolvedSrcPath[AZ_MAX_PATH_LEN] = { 0 };
-        AZ::IO::FileIOBase::GetDirectInstance()->ResolvePath(srcPathNormalized.toUtf8().data(), resolvedSrcPath, AZ_MAX_PATH_LEN);
-        QByteArray path = QDir(rootPathNormalized).relativeFilePath(resolvedSrcPath).toUtf8();
-        if (path.isEmpty())
-        {
-            return fullPath;
-        }
 
-        // The following code is required because the windows PathRelativePathTo function will always return "./SomePath" instead of just "SomePath"
-        // Only remove single dot (.) and slash parts of a path, never the double dot (..)
-        const char* pBuffer = path.data();
-        bool bHasDot = false;
-        while (*pBuffer && pBuffer != path.end())
-        {
-            switch (*pBuffer)
-            {
-            case '.':
-                if (bHasDot)
-                {
-                    // Found a double dot, rewind and stop removing
-                    pBuffer--;
-                    break;
-                }
-            // Fall through intended
-            case '/':
-            case '\\':
-                bHasDot = (*pBuffer == '.');
-                pBuffer++;
-                continue;
-            }
-            break;
-        }
-
-        QString relPath = pBuffer;
-        return CaselessPaths(relPath);
+        return CaselessPaths(resolvedFullPath.LexicallyProximate(rootPath).MakePreferred().c_str());
     }
 
     QString GamePathToFullPath(const QString& path)
     {
         using namespace AzToolsFramework;
-        AZ_Warning("GamePathToFullPath", path.size() <= AZ_MAX_PATH_LEN, "Path exceeds maximum path length of %d", AZ_MAX_PATH_LEN);
-        if ((gEnv) && (gEnv->pFileIO) && gEnv->pCryPak && path.size() <= AZ_MAX_PATH_LEN)
+        AZ_Warning("GamePathToFullPath", path.size() <= AZ::IO::MaxPathLength, "Path exceeds maximum path length of %zu", AZ::IO::MaxPathLength);
+        if (path.size() <= AZ::IO::MaxPathLength)
         {
             // first, adjust the file name for mods:
-            bool fullPathfound = false;
-            AZStd::string assetFullPath;
-            AZStd::string adjustedFilePath = path.toUtf8().data();
-            AssetSystemRequestBus::BroadcastResult(fullPathfound, &AssetSystemRequestBus::Events::GetFullSourcePathFromRelativeProductPath, adjustedFilePath, assetFullPath);
-            if (fullPathfound)
+            bool fullPathFound = false;
+            AZ::IO::Path assetFullPath;
+            AZ::IO::Path adjustedFilePath = path.toUtf8().constData();
+            AssetSystemRequestBus::BroadcastResult(fullPathFound, &AssetSystemRequestBus::Events::GetFullSourcePathFromRelativeProductPath,
+                adjustedFilePath.Native(), assetFullPath.Native());
+            if (fullPathFound)
             {
-                //if the bus message succeeds than normalize and lowercase the path
-                AzFramework::StringFunc::Path::Normalize(assetFullPath);
-                return assetFullPath.c_str();
+                //if the bus message succeeds than normalize
+                return assetFullPath.LexicallyNormal().c_str();
             }
-            // if the bus message didn't succeed, 'guess' the source assets:
+            // if the bus message didn't succeed, check if he path exist as a resolved path
             else
             {
                 // Not all systems have been converted to use local paths. Some editor files save XML files directly, and a full or correctly aliased path is already passed in.
                 // If the path passed in exists already, then return the resolved filepath
                 if (AZ::IO::FileIOBase::GetDirectInstance()->Exists(adjustedFilePath.c_str()))
                 {
-                    char resolvedPath[AZ_MAX_PATH_LEN + PathUtil::maxAliasLength] = { 0 };
-                    AZ::IO::FileIOBase::GetDirectInstance()->ResolvePath(adjustedFilePath.c_str(), resolvedPath, AZ_MAX_PATH_LEN + PathUtil::maxAliasLength);
-                    return QString::fromUtf8(resolvedPath);
+                    AZ::IO::FixedMaxPath resolvedPath;
+                    AZ::IO::FileIOBase::GetDirectInstance()->ResolvePath(resolvedPath, adjustedFilePath);
+                    return QString::fromUtf8(resolvedPath.c_str(), static_cast<int>(resolvedPath.Native().size()));
                 }
-                // if we get here it means that the Asset Processor does not know about this file.  most of the time we should never get here
-                // the rest of this code just does a bunch of heuristic guesses in case of missing files or if the user has hand-edited
-                // the asset cache by moving files in via some other means or external process.
-                if (adjustedFilePath[0] != '@')
-                {
-                    const char* prefix = (adjustedFilePath[0] == '/' || adjustedFilePath[0] == '\\') ? "@devassets@" : "@devassets@/";
-                    adjustedFilePath = prefix + adjustedFilePath;
-                }
-
-                char szAdjustedFile[AZ_MAX_PATH_LEN + PathUtil::maxAliasLength] = { 0 };
-                gEnv->pFileIO->ResolvePath(adjustedFilePath.c_str(), szAdjustedFile, AZ_ARRAY_SIZE(szAdjustedFile));
-
-                if ((azstrnicmp(szAdjustedFile, "@devassets@", 11) == 0) && ((szAdjustedFile[11] == '/') || (szAdjustedFile[11] == '\\')))
-                {
-                    if (!gEnv->pCryPak->IsFileExist(szAdjustedFile))
-                    {
-                        AZStd::string newName(szAdjustedFile);
-                        AzFramework::StringFunc::Replace(newName, "@devassets@", "@devroot@/engine", false);
-                        
-                        if (gEnv->pCryPak->IsFileExist(newName.c_str()))
-                        {
-                            azstrcpy(szAdjustedFile, AZ_ARRAY_SIZE(szAdjustedFile), newName.c_str());
-                        }
-                        else
-                        {
-                            // getting tricky here, try @devroot@ alone, in case its 'editor'
-                            AzFramework::StringFunc::Replace(newName, "@devassets@", "@devroot@", false);
-                            if (gEnv->pCryPak->IsFileExist(szAdjustedFile))
-                            {
-                                azstrcpy(szAdjustedFile, AZ_ARRAY_SIZE(szAdjustedFile), newName.c_str());
-                            }
-                            // give up, best guess is just @devassets@
-                        }
-                    }
-                }
-
-                // we should very rarely actually get to this point in the code.
-
-                // szAdjustedFile may contain an alias at this point. (@assets@/blah.whatever)
-                // there is a case in which the loose asset exists only within a pak file for some reason
-                // this is not recommended but it is possible.in that case, we want to return the original szAdjustedFile
-                // without touching it or resolving it so that crypak can open it successfully.
-                char adjustedPath[AZ_MAX_PATH_LEN + PathUtil::maxAliasLength] = { 0 };
-                if (gEnv->pFileIO->ResolvePath(szAdjustedFile, adjustedPath, AZ_MAX_PATH_LEN + PathUtil::maxAliasLength)) // resolve to full path
-                {
-                    if ((gEnv->pCryPak->IsFileExist(adjustedPath)) || (!gEnv->pCryPak->IsFileExist(szAdjustedFile)))
-                    {
-                        // note that if we get here, then EITHER
-                        // the file exists as a loose asset in the actual adjusted path
-                        // OR the file does not exist in the original passed-in aliased name (like '@assets@/whatever')
-                        // in which case we may as well just resolve the path to a full path and return it.
-                        assetFullPath = adjustedPath;
-                        AzFramework::StringFunc::Path::Normalize(assetFullPath);
-                        azstrcpy(szAdjustedFile, AZ_MAX_PATH_LEN + PathUtil::maxAliasLength, assetFullPath.c_str());
-                    }
-                    // if the above case succeeded then it means that the file does NOT exist loose
-                    // but DOES exist in a pak, in which case we leave szAdjustedFile with the alias on the front of it, meaning
-                    // fopens via crypak will actually succeed.
-                }
-                return szAdjustedFile;
+                return path;
             }
         }
         else
         {
-            return "";
+            return QString{};
         }
     }
 

+ 0 - 90
Code/Editor/Util/PathUtil.h

@@ -44,9 +44,6 @@ namespace Path
     //! always returns a full path
     EDITOR_CORE_API AZStd::string GetEditingGameDataFolder();
 
-    //! Get the root folder (in source control or other writable assets) where you should save root data.
-    EDITOR_CORE_API AZStd::string GetEditingRootFolder();
-
     //! Set the current mod NAME for editing purposes.  After doing this the above functions will take this into account
     //! name only, please!
     EDITOR_CORE_API void SetModName(const char* input);
@@ -69,93 +66,6 @@ namespace Path
         return strPath;
     }
 
-    //! Split full file name to path and filename
-    //! @param filepath [IN] Full file name inclusing path.
-    //! @param path [OUT] Extracted file path.
-    //! @param file [OUT] Extracted file (with extension).
-    inline void Split(const QString& filepath, QString& path, QString& file)
-    {
-        char path_buffer[_MAX_PATH];
-        char drive[_MAX_DRIVE];
-        char dir[_MAX_DIR];
-        char fname[_MAX_FNAME];
-        char ext[_MAX_EXT];
-#ifdef AZ_COMPILER_MSVC
-        _splitpath_s(filepath.toUtf8().data(), drive, AZ_ARRAY_SIZE(drive), dir, AZ_ARRAY_SIZE(dir), fname, AZ_ARRAY_SIZE(fname), ext, AZ_ARRAY_SIZE(ext));
-        _makepath_s(path_buffer, AZ_ARRAY_SIZE(path_buffer), drive, dir, 0, 0);
-        path = path_buffer;
-        _makepath_s(path_buffer, AZ_ARRAY_SIZE(path_buffer), 0, 0, fname, ext);
-#else
-        _splitpath(filepath.toUtf8().data(), drive, dir, fname, ext);
-        _makepath(path_buffer, drive, dir, 0, 0);
-        path = path_buffer;
-        _makepath(path_buffer, 0, 0, fname, ext);
-#endif
-        file = path_buffer;
-    }
-    inline void Split(const AZStd::string& filepath, AZStd::string& path, AZStd::string& file)
-    {
-        char path_buffer[_MAX_PATH];
-        char drive[_MAX_DRIVE];
-        char dir[_MAX_DIR];
-        char fname[_MAX_FNAME];
-        char ext[_MAX_EXT];
-#ifdef AZ_COMPILER_MSVC
-        _splitpath_s(filepath.c_str(), drive, AZ_ARRAY_SIZE(drive), dir, AZ_ARRAY_SIZE(dir), 0, 0, 0, 0);
-        _makepath_s(path_buffer, AZ_ARRAY_SIZE(path_buffer), drive, dir, 0, 0);
-        path = path_buffer;
-        _makepath_s(path_buffer, AZ_ARRAY_SIZE(path_buffer), 0, 0, fname, ext);
-#else
-        _splitpath(filepath.c_str(), drive, dir, fname, ext);
-        _makepath(path_buffer, drive, dir, 0, 0);
-        path = path_buffer;
-        _makepath(path_buffer, 0, 0, fname, ext);
-#endif
-        file = path_buffer;
-    }
-
-    //! Split full file name to path and filename
-    //! @param filepath [IN] Full file name inclusing path.
-    //! @param path [OUT] Extracted file path.
-    //! @param filename [OUT] Extracted file (without extension).
-    //! @param ext [OUT] Extracted files extension.
-    inline void Split(const QString& filepath, QString& path, QString& filename, QString& fext)
-    {
-        char path_buffer[_MAX_PATH];
-        char drive[_MAX_DRIVE];
-        char dir[_MAX_DIR];
-        char fname[_MAX_FNAME];
-        char ext[_MAX_EXT];
-#ifdef AZ_COMPILER_MSVC
-        _splitpath_s(filepath.toUtf8().data(), drive, AZ_ARRAY_SIZE(drive), dir, AZ_ARRAY_SIZE(dir), fname, AZ_ARRAY_SIZE(fname), ext, AZ_ARRAY_SIZE(ext));
-        _makepath_s(path_buffer, AZ_ARRAY_SIZE(path_buffer), drive, dir, 0, 0);
-#else
-        _splitpath(filepath.toUtf8().data(), drive, dir, fname, ext);
-        _makepath(path_buffer, drive, dir, 0, 0);
-#endif
-        path = path_buffer;
-        filename = fname;
-        fext = ext;
-    }
-    inline void Split(const AZStd::string& filepath, AZStd::string& path, AZStd::string& filename, AZStd::string& fext)
-    {
-        char path_buffer[_MAX_PATH];
-        char drive[_MAX_DRIVE];
-        char dir[_MAX_DIR];
-        char fname[_MAX_FNAME];
-        char ext[_MAX_EXT];
-#ifdef AZ_COMPILER_MSVC
-        _splitpath_s(filepath.c_str(), drive, AZ_ARRAY_SIZE(drive), dir, AZ_ARRAY_SIZE(dir), fname, AZ_ARRAY_SIZE(fname), ext, AZ_ARRAY_SIZE(ext));
-        _makepath_s(path_buffer, AZ_ARRAY_SIZE(path_buffer), drive, dir, 0, 0);
-#else
-        _splitpath(filepath.c_str(), drive, dir, fname, ext);
-        _makepath(path_buffer, drive, dir, 0, 0);
-#endif
-        path = path_buffer;
-        filename = fname;
-        fext = ext;
-    }
-
     //! Split path into segments
     //! @param filepath [IN] path
     inline QStringList SplitIntoSegments(const QString& path)

+ 10 - 5
Code/Framework/AzCore/AzCore/IO/FileIO.h

@@ -148,7 +148,7 @@ namespace AZ
             virtual AZ::u64 ModificationTime(HandleType fileHandle) = 0;
             virtual AZ::u64 ModificationTime(const char* filePath) = 0;
 
-            /// Get the size of the file.  Returns Success if we report size. 
+            /// Get the size of the file.  Returns Success if we report size.
             virtual Result Size(const char* filePath, AZ::u64& size) = 0;
             virtual Result Size(HandleType fileHandle, AZ::u64& size) = 0;
 
@@ -198,7 +198,7 @@ namespace AZ
             /// note: the callback will contain the full concatenated path (filePath + slash + fileName)
             ///       not just the individual file name found.
             /// note: if the file path of the found file corresponds to a registered ALIAS, the longest matching alias will be returned
-            ///       so expect return values like @assets@/textures/mytexture.dds instead of a full path.  This is so that fileIO works over remote connections.
+            ///       so expect return values like @products@/textures/mytexture.dds instead of a full path.  This is so that fileIO works over remote connections.
             /// note: if rootPath is specified the implementation has the option of substituting it for the current directory
             ///      as would be the case on a file server.
             typedef AZStd::function<bool(const char*)> FindFilesCallbackType;
@@ -206,13 +206,18 @@ namespace AZ
 
             // Alias system
 
-            /// SetAlias - Adds an alias to the path resolution system, e.g. @user@, @root@, etc.
+            /// SetAlias - Adds an alias to the path resolution system, e.g. @user@, @products@, etc.
             virtual void SetAlias(const char* alias, const char* path) = 0;
             /// ClearAlias - Removes an alias from the path resolution system
             virtual void ClearAlias(const char* alias) = 0;
             /// GetAlias - Returns the destination path for a given alias, or nullptr if the alias does not exist
             virtual const char* GetAlias(const char* alias) const = 0;
 
+            /// SetDeprecateAlias - Adds a deprecated alias with path resolution which points to a new alias
+            /// When the DeprecatedAlias is used an Error is logged and the alias is resolved to the path
+            /// specified by the new alais
+            virtual void SetDeprecatedAlias(AZStd::string_view oldAlias, AZStd::string_view newAlias) = 0;
+
             /// Shorten the given path if it contains an alias.  it will always pick the longest alias match.
             /// note that it re-uses the buffer, since the data can only get smaller and we don't want to internally allocate memory if we
             /// can avoid it.
@@ -230,8 +235,8 @@ namespace AZ
 
             //! ResolvePath - Replaces any aliases in path with their values and stores the result in resolvedPath,
             //! also ensures that the path is absolute
-            //! NOTE: If the path does not start with an alias then the resolved value of the @assets@ is used
-            //!       which has the effect of making the path relative to the @assets@/ folder
+            //! NOTE: If the path does not start with an alias then the resolved value of the @products@ is used
+            //!       which has the effect of making the path relative to the @products@/ folder
             //! returns true if path was resolved, false otherwise
             //! note that all of the above file-finding and opening functions automatically resolve the path before operating
             //! so you should not need to call this except in very exceptional circumstances where you absolutely need to

+ 16 - 16
Code/Framework/AzCore/AzCore/IO/IStreamer.h

@@ -42,8 +42,8 @@ namespace AZ::IO
         // These functions can't be called after a request has been queued.
         //
 
-        //! Creates a request to read a file. 
-        //! @param relativePath Relative path to the file to load. This can include aliases such as @assets@.
+        //! Creates a request to read a file.
+        //! @param relativePath Relative path to the file to load. This can include aliases such as @products@.
         //! @param outputBuffer The buffer that will hold the loaded data. This must be able to at least hold "size" number of bytes.
         //! @param outputBufferSize The size of the buffer that will hold the loaded data. This must be equal or larger than "size" number of bytes.
         //! @param readSize The number of bytes to read from the file at the relative path.
@@ -62,9 +62,9 @@ namespace AZ::IO
             IStreamerTypes::Priority priority = IStreamerTypes::s_priorityMedium,
             size_t offset = 0) = 0;
 
-        //! Sets a request to the read command. 
+        //! Sets a request to the read command.
         //! @param request The request that will store the read command.
-        //! @param relativePath Relative path to the file to load. This can include aliases such as @assets@.
+        //! @param relativePath Relative path to the file to load. This can include aliases such as @products@.
         //! @param outputBuffer The buffer that will hold the loaded data. This must be able to at least hold "size" number of bytes.
         //! @param outputBufferSize The size of the buffer that will hold the loaded data. This must be equal or larger than "size" number of bytes.
         //! @param readSize The number of bytes to read from the file at the relative path.
@@ -84,8 +84,8 @@ namespace AZ::IO
             IStreamerTypes::Priority priority = IStreamerTypes::s_priorityMedium,
             size_t offset = 0) = 0;
 
-        //! Creates a request to the read command. 
-        //! @param relativePath Relative path to the file to load. This can include aliases such as @assets@.
+        //! Creates a request to the read command.
+        //! @param relativePath Relative path to the file to load. This can include aliases such as @products@.
         //! @param allocator The allocator used to reserve and release memory for the read request. Memory allocated this way will
         //!         be automatically freed when there are no more references to the FileRequestPtr. To avoid this, use GetReadRequestResult
         //!         to claim the pointer and use the provided allocator to release the memory at a later point.
@@ -106,9 +106,9 @@ namespace AZ::IO
             IStreamerTypes::Priority priority = IStreamerTypes::s_priorityMedium,
             size_t offset = 0) = 0;
 
-        //! Sets a request to the read command. 
+        //! Sets a request to the read command.
         //! @param request The request that will store the read command.
-        //! @param relativePath Relative path to the file to load. This can include aliases such as @assets@.
+        //! @param relativePath Relative path to the file to load. This can include aliases such as @products@.
         //! @param allocator The allocator used to reserve and release memory for the read request. Memory allocated this way will
         //!         be automatically freed when there are no more references to the FileRequestPtr. To avoid this, use GetReadRequestResult
         //!         to claim the pointer and use the provided allocator to release the memory at a later point.
@@ -138,7 +138,7 @@ namespace AZ::IO
         //! @result A smart pointer to the newly created request with the cancel command.
         virtual FileRequestPtr Cancel(FileRequestPtr target) = 0;
 
-        //! Sets a request to the cancel command. 
+        //! Sets a request to the cancel command.
         //! When this request completes it's not guaranteed to have canceled the target request. Not all requests can be canceled and requests
         //! that already processing may complete. It's recommended to let the target request handle the completion of the request as normal
         //! and handle cancellation by checking the status on the target request is set to IStreamerTypes::RequestStatus::Canceled.
@@ -177,7 +177,7 @@ namespace AZ::IO
         //! DestroyDedicatedCache is called. Typical use of a dedicated cache is for files that have their own compression
         //! and are periodically visited to read a section, e.g. streaming video play or streaming audio banks. This
         //! request will fail if there are no nodes in Streamer's stack that deal with dedicated caches.
-        //! @param relativePath Relative path to the file to receive a dedicated cache. This can include aliases such as @assets@.
+        //! @param relativePath Relative path to the file to receive a dedicated cache. This can include aliases such as @products@.
         //! @return A smart pointer to the newly created request with the command to create a dedicated cache.
         virtual FileRequestPtr CreateDedicatedCache(AZStd::string_view relativePath) = 0;
 
@@ -186,25 +186,25 @@ namespace AZ::IO
         //! and are periodically visited to read a section, e.g. streaming video play or streaming audio banks. This
         //! request will fail if there are no nodes in Streamer's stack that deal with dedicated caches.
         //! @param request The request that will store the command to create a dedicated cache.
-        //! @param relativePath Relative path to the file to receive a dedicated cache. This can include aliases such as @assets@.
+        //! @param relativePath Relative path to the file to receive a dedicated cache. This can include aliases such as @products@.
         //! @return A reference to the provided request.
         virtual FileRequestPtr& CreateDedicatedCache(FileRequestPtr& request, AZStd::string_view relativePath) = 0;
 
         //! Destroy a dedicated cache created by CreateDedicatedCache. See CreateDedicatedCache for more details.
-        //! @param relativePath Relative path to the file that got a dedicated cache. This can include aliases such as @assets@.
+        //! @param relativePath Relative path to the file that got a dedicated cache. This can include aliases such as @products@.
         //! @return A smart pointer to the newly created request with the command to destroy a dedicated cache.
         virtual FileRequestPtr DestroyDedicatedCache(AZStd::string_view relativePath) = 0;
 
         //! Destroy a dedicated cache created by CreateDedicatedCache. See CreateDedicatedCache for more details.
         //! @param request The request that will store the command to destroy a dedicated cache.
-        //! @param relativePath Relative path to the file that got a dedicated cache. This can include aliases such as @assets@.
+        //! @param relativePath Relative path to the file that got a dedicated cache. This can include aliases such as @products@.
         //! @return A reference to the provided request.
         virtual FileRequestPtr& DestroyDedicatedCache(FileRequestPtr& request, AZStd::string_view relativePath) = 0;
 
         //! Clears a file from all caches in use by Streamer.
         //! Flushing the cache will cause the streaming stack to pause processing until it's idle before issuing the flush and resuming
         //! processing. This can result in a noticeable interruption.
-        //! @param relativePath Relative path to the file that will be cleared from all caches. This can include aliases such as @assets@.
+        //! @param relativePath Relative path to the file that will be cleared from all caches. This can include aliases such as @products@.
         //! @return A smart pointer to the newly created request with the command to flush a file from all caches.
         virtual FileRequestPtr FlushCache(AZStd::string_view relativePath) = 0;
 
@@ -212,7 +212,7 @@ namespace AZ::IO
         //! Flushing the cache will cause the streaming stack to pause processing until it's idle before issuing the flush and resuming
         //! processing. This can result in a noticeable interruption.
         //! @param request The request that will store the command to flush a file from all caches.
-        //! @param relativePath Relative path to the file that will be cleared from all caches. This can include aliases such as @assets@.
+        //! @param relativePath Relative path to the file that will be cleared from all caches. This can include aliases such as @products@.
         //! @return A reference to the provided request.
         virtual FileRequestPtr& FlushCache(FileRequestPtr& request, AZStd::string_view relativePath) = 0;
 
@@ -334,7 +334,7 @@ namespace AZ::IO
         //
 
         //! Collect statistics from all the components that make up Streamer.
-        //! This is thread safe in the sense that it won't crash. 
+        //! This is thread safe in the sense that it won't crash.
         //! Data is collected lockless from involved threads and might be slightly
         //! out of date in some cases.
         //! @param statistics The container where statistics will be added to.

+ 31 - 21
Code/Framework/AzCore/AzCore/IO/Path/Path.h

@@ -98,6 +98,11 @@ namespace AZ::IO
         //! made from the internal string
         constexpr AZStd::fixed_string<MaxPathLength> FixedMaxPathString() const noexcept;
 
+        // as_posix
+        //! Replicates the behavior of the Python pathlib as_posix method
+        //! by replacing the Windows Path Separator with the Posix Path Seperator
+        constexpr AZStd::fixed_string<MaxPathLength> FixedMaxPathStringAsPosix() const noexcept;
+
         // decomposition
         //! Given a windows path of "C:\O3DE\foo\bar\name.txt" and a posix path of
         //! "/O3DE/foo/bar/name.txt"
@@ -178,7 +183,7 @@ namespace AZ::IO
         //! Normalizes a path in a purely lexical manner.
         //! # Path separators are converted to their preferred path separator
         //! # Path parts of "." are collapsed to nothing empty
-        //! # Paths parts of ".." are removed if there is a preceding directory 
+        //! # Paths parts of ".." are removed if there is a preceding directory
         //! The preceding directory is also removed
         //! # Runs of Two or more path separators are collapsed into one path separator
         //! unless the path begins with two path separators
@@ -238,7 +243,7 @@ namespace AZ::IO
 
         // iterators
         //! Returns an iterator to the beginning of the path that can be used to traverse the path
-        //! according to the following 
+        //! according to the following
         //! 1. Root name - (0 or 1)
         //! 2. Root directory - (0 or 1)
         //! 3. Filename - (0 or more)
@@ -253,24 +258,23 @@ namespace AZ::IO
         template <typename StringType>
         friend class BasicPath;
         friend struct AZStd::hash<PathView>;
+        struct PathIterable;
 
-        template <typename PathResultType>
-        static constexpr void MakeRelativeTo(PathResultType& pathResult, const AZ::IO::PathView& path, const AZ::IO::PathView& base);
+        static constexpr void MakeRelativeTo(PathIterable& pathResult, const AZ::IO::PathView& path, const AZ::IO::PathView& base) noexcept;
 
-        struct PathIterable;
         //! Returns a structure that provides a view of the path parts which can be used for iteration
         //! Only the path parts that correspond to creating an normalized path is returned
         //! This function is useful for returning a "view" into a normalized path without the need
         //! to allocate memory for the heap
         static constexpr PathIterable GetNormalPathParts(const AZ::IO::PathView& path) noexcept;
-        // joins the input path to the Path Iterable structure using similiar logic to Path::Append
-        // If the input path is absolute it will replace the current PathIterable otherwise
-        // the input path will be appended to the Path Iterable structure
-        // For example a PathIterable with parts = ['C:', '/', 'foo']
-        // If the path input = 'bar', then the new PathIterable parts = [C:', '/', 'foo', 'bar']
-        // If the path input = 'C:/bar', then the new PathIterable parts = [C:', '/', 'bar']
-        // If the path input = 'C:bar', then the new PathIterable parts = [C:', '/', 'foo', 'bar' ]
-        // If the path input = 'D:bar', then the new PathIterable parts = [D:, 'bar' ]
+        //! joins the input path to the Path Iterable structure using similiar logic to Path::Append
+        //! If the input path is absolute it will replace the current PathIterable otherwise
+        //! the input path will be appended to the Path Iterable structure
+        //! For example a PathIterable with parts = ['C:', '/', 'foo']
+        //! If the path input = 'bar', then the new PathIterable parts = [C:', '/', 'foo', 'bar']
+        //! If the path input = 'C:/bar', then the new PathIterable parts = [C:', '/', 'bar']
+        //! If the path input = 'C:bar', then the new PathIterable parts = [C:', '/', 'foo', 'bar' ]
+        //! If the path input = 'D:bar', then the new PathIterable parts = [D:, 'bar' ]
         static constexpr void AppendNormalPathParts(PathIterable& pathIterableResult, const AZ::IO::PathView& path) noexcept;
 
         constexpr int ComparePathView(const PathView& other) const;
@@ -325,32 +329,32 @@ namespace AZ::IO
         constexpr BasicPath(BasicPath&& other) = default;
 
         // Conversion constructor for other types of BasicPath instantiations
-        constexpr BasicPath(const PathView& other);
+        constexpr BasicPath(const PathView& other) noexcept;
 
         // String constructors
         //! Constructs a Path by copying the pathString to its internal string
         //! The preferred separator is to the OS default path separator
         constexpr BasicPath(const string_type& pathString) noexcept;
         //! Constructs a Path by copying the pathString to its internal string
-        //! The preferred separator it set to the parameter
+        //! The preferred separator is set to the parameter
         constexpr BasicPath(const string_type& pathString, const char preferredSeparator) noexcept;
         //! Constructs a Path by moving the pathString to its internal string
         //! The preferred separator is to the OS default path separator
         constexpr BasicPath(string_type&& pathString) noexcept;
         //! Constructs a Path by copying the pathString to its internal string
-        //! The preferred separator it set to the parameter
+        //! The preferred separator is set to the parameter
         constexpr BasicPath(string_type&& pathString, const char preferredSeparator) noexcept;
         //! Constructs a Path by constructing it's internal out of a string_view
         //! The preferred separator is to the OS default path separator
         constexpr BasicPath(AZStd::string_view src) noexcept;
         //! Constructs a Path by constructing it's internal out of a string_view
-        //! The preferred separators it set to the parameter
+        //! The preferred separator is set to the parameter
         constexpr BasicPath(AZStd::string_view src, const char preferredSeparator) noexcept;
         //! Constructs a Path by constructing it's internal out of a value_type*
         //! The preferred separator is to the OS default path separator
         constexpr BasicPath(const value_type* pathString) noexcept;
         //! Constructs a Path by constructing it's internal out of a value_type*
-        //! The preferred separator it set to the parameter
+        //! The preferred separator is set to the parameter
         constexpr BasicPath(const value_type* pathString, const char preferredSeparator) noexcept;
         //! Constructs a empty Path with the preferred separator set to the parameter
         explicit constexpr BasicPath(const char preferredSeparator) noexcept;
@@ -371,7 +375,7 @@ namespace AZ::IO
         constexpr BasicPath& operator=(BasicPath&& other) = default;
 
         // conversion assignment operator
-        constexpr BasicPath& operator=(const PathView& pathView);
+        constexpr BasicPath& operator=(const PathView& pathView) noexcept;
         constexpr BasicPath& operator=(const string_type& str) noexcept;
         constexpr BasicPath& operator=(string_type&& str) noexcept;
         constexpr BasicPath& operator=(AZStd::string_view str) noexcept;
@@ -477,6 +481,12 @@ namespace AZ::IO
         //! made from the internal string
         constexpr AZStd::fixed_string<MaxPathLength> FixedMaxPathString() const;
 
+        // as_posix
+        //! Replicates the behavior of the Python pathlib as_posix method
+        //! by replacing the Windows Path Separator with the Posix Path Seperator
+        AZStd::string StringAsPosix() const;
+        constexpr AZStd::fixed_string<MaxPathLength> FixedMaxPathStringAsPosix() const noexcept;
+
         // compare
         //! Performs a compare of each of the path parts for equivalence
         //! Each part of the path is compare using string comparison
@@ -574,7 +584,7 @@ namespace AZ::IO
         //! Normalizes a path in a purely lexical manner.
         //! # Path separators are converted to their preferred path separator
         //! # Path parts of "." are collapsed to nothing empty
-        //! # Paths parts of ".." are removed if there is a preceding directory 
+        //! # Paths parts of ".." are removed if there is a preceding directory
         //! The preceding directory is also removed
         //! # Runs of Two or more path separators are collapsed into one path separator
         //! unless the path begins with two path separators
@@ -616,7 +626,7 @@ namespace AZ::IO
 
         // iterators
         //! Returns an iterator to the beginning of the path that can be used to traverse the path
-        //! according to the following 
+        //! according to the following
         //! 1. Root name - (0 or 1)
         //! 2. Root directory - (0 or 1)
         //! 3. Filename - (0 or more)

+ 59 - 24
Code/Framework/AzCore/AzCore/IO/Path/Path.inl

@@ -240,6 +240,14 @@ namespace AZ::IO
         return AZStd::fixed_string<MaxPathLength>(m_path.begin(), m_path.end());
     }
 
+    // as_posix
+    constexpr AZStd::fixed_string<MaxPathLength> PathView::FixedMaxPathStringAsPosix() const noexcept
+    {
+        AZStd::fixed_string<MaxPathLength> resultPath(m_path.begin(), m_path.end());
+        AZStd::replace(resultPath.begin(), resultPath.end(), AZ::IO::WindowsPathSeparator, AZ::IO::PosixPathSeparator);
+        return resultPath;
+    }
+
     // decomposition
     constexpr auto PathView::RootName() const -> PathView
     {
@@ -473,8 +481,7 @@ namespace AZ::IO
         return lhs.Compare(rhs) >= 0;
     }
 
-    template <typename PathResultType>
-    constexpr void PathView::MakeRelativeTo(PathResultType& pathResult, const AZ::IO::PathView& path, const AZ::IO::PathView& base)
+    constexpr void PathView::MakeRelativeTo(PathIterable& pathIterable, const AZ::IO::PathView& path, const AZ::IO::PathView& base) noexcept
     {
         const bool exactCaseCompare = path.m_preferred_separator == PosixPathSeparator
             || base.m_preferred_separator == PosixPathSeparator;
@@ -492,13 +499,11 @@ namespace AZ::IO
                 if (int res = Internal::ComparePathSegment(*pathParser, *pathParserBase, exactCaseCompare);
                     res != 0)
                 {
-                    pathResult.m_path = AZStd::string_view{};
                     return;
                 }
             }
             else if (CheckIterMismatchAtBase())
             {
-                pathResult.m_path = AZStd::string_view{};
                 return;
             }
 
@@ -512,7 +517,6 @@ namespace AZ::IO
             }
             if (CheckIterMismatchAtBase())
             {
-                pathResult.m_path = AZStd::string_view{};
                 return;
             }
         }
@@ -530,7 +534,7 @@ namespace AZ::IO
         // If there is no mismatch, return ".".
         if (!pathParser && !pathParserBase)
         {
-            pathResult.m_path = AZStd::string_view{ "." };
+            pathIterable.emplace_back(".", parser::PathPartKind::PK_Dot);
             return;
         }
 
@@ -539,27 +543,25 @@ namespace AZ::IO
         int elemCount = parser::DetermineLexicalElementCount(pathParserBase);
         if (elemCount < 0)
         {
-            pathResult.m_path = AZStd::string_view{};
             return;
         }
 
         // if elemCount == 0 and (pathParser == end() || pathParser->empty()), returns path("."); otherwise
         if (elemCount == 0 && (pathParser.AtEnd() || *pathParser == ""))
         {
-            pathResult.m_path = AZStd::string_view{ "." };
+            pathIterable.emplace_back(".", parser::PathPartKind::PK_Dot);
             return;
         }
 
         // return a path constructed with 'n' dot-dot elements, followed by the
         // elements of '*this' after the mismatch.
-        pathResult = PathResultType(path.m_preferred_separator);
         while (elemCount--)
         {
-            pathResult /= "..";
+            pathIterable.emplace_back("..", parser::PathPartKind::PK_DotDot);
         }
         for (; pathParser; ++pathParser)
         {
-            pathResult /= *pathParser;
+            pathIterable.emplace_back(*pathParser, parser::ClassifyPathPart(pathParser));
         }
     }
 
@@ -673,7 +675,7 @@ namespace AZ::IO
     // Basic Path implementation
 
     template <typename StringType>
-    constexpr BasicPath<StringType>::BasicPath(const PathView& other)
+    constexpr BasicPath<StringType>::BasicPath(const PathView& other) noexcept
         : m_path(other.m_path)
         , m_preferred_separator(other.m_preferred_separator) {}
 
@@ -726,6 +728,7 @@ namespace AZ::IO
         : m_path(first, last)
         , m_preferred_separator(preferredSeparator) {}
 
+
     template <typename StringType>
     constexpr BasicPath<StringType>::operator PathView() const noexcept
     {
@@ -733,7 +736,7 @@ namespace AZ::IO
     }
 
     template <typename StringType>
-    constexpr auto BasicPath<StringType>::operator=(const PathView& other) -> BasicPath&
+    constexpr auto BasicPath<StringType>::operator=(const PathView& other) noexcept -> BasicPath&
     {
         m_path = other.m_path;
         m_preferred_separator = other.m_preferred_separator;
@@ -974,13 +977,13 @@ namespace AZ::IO
     template <typename StringType>
     constexpr auto BasicPath<StringType>::MakePreferred() -> BasicPath&
     {
-        if (m_preferred_separator != '/')
+        if (m_preferred_separator != PosixPathSeparator)
         {
-            AZStd::replace(m_path.begin(), m_path.end(), '/', m_preferred_separator);
+            AZStd::replace(m_path.begin(), m_path.end(), PosixPathSeparator, m_preferred_separator);
         }
         else
         {
-            AZStd::replace(m_path.begin(), m_path.end(), '\\', m_preferred_separator);
+            AZStd::replace(m_path.begin(), m_path.end(), WindowsPathSeparator, m_preferred_separator);
         }
         return *this;
     }
@@ -1033,6 +1036,24 @@ namespace AZ::IO
         return AZStd::fixed_string<MaxPathLength>(m_path.begin(), m_path.end());
     }
 
+    // as_posix
+    // Returns a copy of the path with the path separators converted to PosixPathSeparator
+    template <typename StringType>
+    AZStd::string BasicPath<StringType>::StringAsPosix() const
+    {
+        AZStd::string resultPath(m_path.begin(), m_path.end());
+        AZStd::replace(resultPath.begin(), resultPath.end(), WindowsPathSeparator, PosixPathSeparator);
+        return resultPath;
+    }
+
+    template <typename StringType>
+    constexpr AZStd::fixed_string<MaxPathLength> BasicPath<StringType>::FixedMaxPathStringAsPosix() const noexcept
+    {
+        AZStd::fixed_string<MaxPathLength> resultPath(m_path.begin(), m_path.end());
+        AZStd::replace(resultPath.begin(), resultPath.end(), WindowsPathSeparator, PosixPathSeparator);
+        return resultPath;
+    }
+
     template <typename StringType>
     constexpr void BasicPath<StringType>::swap(BasicPath& rhs) noexcept
     {
@@ -1234,6 +1255,7 @@ namespace AZ::IO
         {
             pathResult /= pathPartView;
         }
+
         return pathResult;
     }
 
@@ -1241,7 +1263,13 @@ namespace AZ::IO
     constexpr auto BasicPath<StringType>::LexicallyRelative(const PathView& base) const -> BasicPath
     {
         BasicPath pathResult(m_preferred_separator);
-        static_cast<PathView>(*this).MakeRelativeTo(pathResult, *this, base);
+        PathView::PathIterable pathIterable;
+        PathView::MakeRelativeTo(pathIterable, *this, base);
+        for ([[maybe_unused]] auto [pathPartView, pathPartKind] : pathIterable)
+        {
+            pathResult /= pathPartView;
+        }
+
         return pathResult;
     }
 
@@ -1355,7 +1383,7 @@ namespace AZ::IO
         return !basePathParts.empty() || !thisPathParts.IsAbsolute();
     }
 
-    constexpr FixedMaxPath PathView::LexicallyNormal() const
+    constexpr auto PathView::LexicallyNormal() const -> FixedMaxPath
     {
         FixedMaxPath pathResult(m_preferred_separator);
         PathIterable pathIterable = GetNormalPathParts(*this);
@@ -1367,21 +1395,28 @@ namespace AZ::IO
         return pathResult;
     }
 
-    constexpr FixedMaxPath PathView::LexicallyRelative(const PathView& base) const
+    constexpr auto PathView::LexicallyRelative(const PathView& base) const -> FixedMaxPath
     {
         FixedMaxPath pathResult(m_preferred_separator);
-        MakeRelativeTo(pathResult, *this, base);
+        PathIterable pathIterable;
+        MakeRelativeTo(pathIterable, *this, base);
+        for ([[maybe_unused]] auto [pathPartView, pathPartKind] : pathIterable)
+        {
+            pathResult /= pathPartView;
+        }
+
         return pathResult;
     }
 
-    constexpr FixedMaxPath PathView::LexicallyProximate(const PathView& base) const
+    constexpr auto PathView::LexicallyProximate(const PathView& base) const -> FixedMaxPath
     {
-        FixedMaxPath result = LexicallyRelative(base);
-        if (result.empty())
+        FixedMaxPath pathResult = LexicallyRelative(base);
+        if (pathResult.empty())
         {
             return FixedMaxPath(*this);
         }
-        return result;
+
+        return pathResult;
     }
 }
 

+ 1 - 1
Code/Framework/AzCore/AzCore/IO/Path/PathIterable.inl

@@ -49,8 +49,8 @@ namespace AZ::IO
 
         constexpr void clear() noexcept;
 
-        friend constexpr auto PathView::GetNormalPathParts(const AZ::IO::PathView&) noexcept -> PathIterable;
         friend constexpr auto PathView::AppendNormalPathParts(PathIterable& pathIterable, const AZ::IO::PathView&) noexcept -> void;
+        friend constexpr auto PathView::MakeRelativeTo(PathIterable& pathIterable, const AZ::IO::PathView&, const AZ::IO::PathView&) noexcept -> void;
         PartKindArray m_parts{};
         size_t m_size{};
     };

+ 3 - 3
Code/Framework/AzCore/AzCore/Settings/SettingsRegistryMergeUtils.cpp

@@ -546,7 +546,7 @@ namespace AZ::SettingsRegistryMergeUtils
         AZ::IO::FixedMaxPath path = AZ::Utils::GetExecutableDirectory();
         registry.Set(FilePathKey_BinaryFolder, path.LexicallyNormal().Native());
 
-        // Engine root folder - corresponds to the @engroot@ and @devroot@ aliases
+        // Engine root folder - corresponds to the @engroot@ and @engroot@ aliases
         AZ::IO::FixedMaxPath engineRoot = FindEngineRoot(registry);
         registry.Set(FilePathKey_EngineRootFolder, engineRoot.LexicallyNormal().Native());
 
@@ -570,7 +570,7 @@ namespace AZ::SettingsRegistryMergeUtils
                 assetPlatform = AZ::OSPlatformToDefaultAssetPlatform(AZ_TRAIT_OS_PLATFORM_CODENAME);
             }
 
-            // Project path - corresponds to the @devassets@ alias
+            // Project path - corresponds to the @projectroot@ alias
             // NOTE: Here we append to engineRoot, but if projectPathValue is absolute then engineRoot is discarded.
             path = engineRoot / projectPathValue;
 
@@ -662,7 +662,7 @@ namespace AZ::SettingsRegistryMergeUtils
                 }
                 else
                 {
-                    // Cache: root - same as the @root@ alias, this is the starting path for cache files.
+                    // Cache: root - same as the @products@ alias, this is the starting path for cache files.
                     path = normalizedProjectPath / "Cache";
                     registry.Set(FilePathKey_CacheProjectRootFolder, path.LexicallyNormal().Native());
                     path /= assetPlatform;

+ 1 - 0
Code/Framework/AzCore/AzCore/UnitTest/Mocks/MockFileIOBase.h

@@ -52,6 +52,7 @@ namespace AZ
             MOCK_METHOD2(SetAlias,    void(const char* alias, const char* path));
             MOCK_METHOD1(ClearAlias,  void(const char* alias));
             MOCK_CONST_METHOD1(GetAlias,    const char*(const char* alias));
+            MOCK_METHOD2(SetDeprecatedAlias, void(AZStd::string_view, AZStd::string_view));
             MOCK_CONST_METHOD2(ConvertToAlias, AZStd::optional<AZ::u64>(char* inOutBuffer, AZ::u64 bufferLength));
             MOCK_CONST_METHOD2(ConvertToAlias, bool(AZ::IO::FixedMaxPath& aliasPath, const AZ::IO::PathView& path));
             MOCK_CONST_METHOD3(ResolvePath, bool(const char* path, char* resolvedPath, AZ::u64 resolvedPathSize));

+ 14 - 0
Code/Framework/AzCore/AzCore/Utils/Utils.cpp

@@ -51,6 +51,20 @@ namespace AZ::Utils
         return executableDirectory;
     }
 
+    AZStd::optional<AZ::IO::FixedMaxPathString> ConvertToAbsolutePath(AZStd::string_view path)
+    {
+        AZ::IO::FixedMaxPathString absolutePath;
+        AZ::IO::FixedMaxPathString srcPath{ path };
+        if (ConvertToAbsolutePath(srcPath.c_str(), absolutePath.data(), absolutePath.capacity()))
+        {
+            // Fix the size value of the fixed string by calculating the c-string length using char traits
+            absolutePath.resize_no_construct(AZStd::char_traits<char>::length(absolutePath.data()));
+            return srcPath;
+        }
+
+        return AZStd::nullopt;
+    }
+
     AZ::IO::FixedMaxPathString GetEngineManifestPath()
     {
         AZ::IO::FixedMaxPath o3deManifestPath = GetO3deManifestDirectory();

+ 1 - 0
Code/Framework/AzCore/AzCore/Utils/Utils.h

@@ -104,6 +104,7 @@ namespace AZ
         // Attempts the supplied path to an absolute path.
         //! Returns nullopt if path cannot be converted to an absolute path
         AZStd::optional<AZ::IO::FixedMaxPathString> ConvertToAbsolutePath(AZStd::string_view path);
+        bool ConvertToAbsolutePath(const char* path, char* absolutePath, AZ::u64 absolutePathMaxSize);
 
         //! Save a string to a file. Otherwise returns a failure with error message.
         AZ::Outcome<void, AZStd::string> WriteFile(AZStd::string_view content, AZStd::string_view filePath);

+ 22 - 11
Code/Framework/AzCore/Platform/Android/AzCore/Utils/Utils_Android.cpp

@@ -60,23 +60,34 @@ namespace AZ
             return writeStorage ? AZStd::make_optional<AZ::IO::FixedMaxPathString>(writeStorage) : AZStd::nullopt;
         }
 
-        AZStd::optional<AZ::IO::FixedMaxPathString> ConvertToAbsolutePath(AZStd::string_view path)
+        bool ConvertToAbsolutePath(const char* path, char* absolutePath, AZ::u64 maxLength)
         {
-            AZ::IO::FixedMaxPathString absolutePath;
-            AZ::IO::FixedMaxPathString srcPath{ path };
-            if (AZ::Android::Utils::IsApkPath(srcPath.c_str()))
+            if (AZ::Android::Utils::IsApkPath(path))
             {
-                return srcPath;
+                azstrcpy(absolutePath, maxLength, path);
+                return true;
             }
 
-            if(char* result = realpath(srcPath.c_str(), absolutePath.data()); result)
+#ifdef PATH_MAX
+            static constexpr size_t UnixMaxPathLength = PATH_MAX;
+#else
+            // Fallback to 4096 if the PATH_MAX macro isn't defined on the Unix System
+            static constexpr size_t UnixMaxPathLength = 4096;
+#endif
+            if (!AZ::IO::PathView(path).IsAbsolute())
             {
-                // Fix the size value of the fixed string by calculating the c-string length using char traits
-                absolutePath.resize_no_construct(AZStd::char_traits<char>::length(absolutePath.data()));
-                return absolutePath;
+                // note that realpath fails if the path does not exist and actually changes the return value
+                // to be the actual place that FAILED, which we don't want.
+                // if we fail, we'd prefer to fall through and at least use the original path.
+                char absolutePathBuffer[UnixMaxPathLength];
+                if (const char* result = realpath(path, absolutePathBuffer); result != nullptr)
+                {
+                    azstrcpy(absolutePath, maxLength, absolutePathBuffer);
+                    return true;
+                }
             }
-
-            return AZStd::nullopt;
+            azstrcpy(absolutePath, maxLength, path);
+            return AZ::IO::PathView(absolutePath).IsAbsolute();
         }
     }
 }

+ 20 - 11
Code/Framework/AzCore/Platform/Common/UnixLike/AzCore/Utils/Utils_UnixLike.cpp

@@ -47,23 +47,32 @@ namespace AZ
                 AZ::IO::FixedMaxPath path{pass->pw_dir};
                 return path.Native();
             }
-            
+
             return {};
         }
 
-        AZStd::optional<AZ::IO::FixedMaxPathString> ConvertToAbsolutePath(AZStd::string_view path)
+        bool ConvertToAbsolutePath(const char* path, char* absolutePath, AZ::u64 maxLength)
         {
-            AZ::IO::FixedMaxPathString absolutePath;
-            AZ::IO::FixedMaxPathString srcPath{ path };
-
-            if (char* result = realpath(srcPath.c_str(), absolutePath.data()); result)
+#ifdef PATH_MAX
+            static constexpr size_t UnixMaxPathLength = PATH_MAX;
+#else
+            // Fallback to 4096 if the PATH_MAX macro isn't defined on the Unix System
+            static constexpr size_t UnixMaxPathLength = 4096;
+#endif
+            if (!AZ::IO::PathView(path).IsAbsolute())
             {
-                // Fix the size value of the fixed string by calculating the c-string length using char traits
-                absolutePath.resize_no_construct(AZStd::char_traits<char>::length(absolutePath.data()));
-                return absolutePath;
+                // note that realpath fails if the path does not exist and actually changes the return value
+                // to be the actual place that FAILED, which we don't want.
+                // if we fail, we'd prefer to fall through and at least use the original path.
+                char absolutePathBuffer[UnixMaxPathLength];
+                if (const char* result = realpath(path, absolutePathBuffer); result != nullptr)
+                {
+                    azstrcpy(absolutePath, maxLength, absolutePathBuffer);
+                    return true;
+                }
             }
-
-            return AZStd::nullopt;
+            azstrcpy(absolutePath, maxLength, path);
+            return AZ::IO::PathView(absolutePath).IsAbsolute();
         }
     } // namespace Utils
 } // namespace AZ

+ 5 - 12
Code/Framework/AzCore/Platform/Common/WinAPI/AzCore/Utils/Utils_WinAPI.cpp

@@ -67,19 +67,12 @@ namespace AZ
             return AZStd::nullopt;
         }
 
-        AZStd::optional<AZ::IO::FixedMaxPathString> ConvertToAbsolutePath(AZStd::string_view path)
+        bool ConvertToAbsolutePath(const char* path, char* absolutePath, AZ::u64 maxLength)
         {
-            AZ::IO::FixedMaxPathString absolutePath;
-            AZ::IO::FixedMaxPathString srcPath{ path };
-            char* result = _fullpath(absolutePath.data(), srcPath.c_str(), absolutePath.capacity());
-            // Force update of the fixed_string size() value
-            absolutePath.resize_no_construct(AZStd::char_traits<char>::length(absolutePath.data()));
-            if (result)
-            {
-                return absolutePath;
-            }
-
-            return AZStd::nullopt;
+            char* result = _fullpath(absolutePath, path, maxLength);
+            return result != nullptr;
         }
+
+
     }
 }

+ 4 - 0
Code/Framework/AzCore/Tests/FileIOBaseTestTypes.h

@@ -426,6 +426,10 @@ public:
         return nullptr;
     }
 
+    void SetDeprecatedAlias(AZStd::string_view, AZStd::string_view) override
+    {
+    }
+
     void ClearAlias(const char* ) override { }
 
     AZStd::optional<AZ::u64> ConvertToAlias(char* inOutBuffer, AZ::u64) const override

+ 1 - 1
Code/Framework/AzCore/Tests/IO/Path/PathTests.cpp

@@ -698,7 +698,7 @@ AZ_POP_DISABLE_WARNING
 
     using PathViewLexicallyProximateFixture = PathLexicallyFixture<PathViewLexicallyProximateParams>;
 
-    TEST_P(PathViewLexicallyProximateFixture, LexicallyProximate_ReturnsRelativePathIfNotEmptyOrTestPathIfNot)
+    TEST_P(PathViewLexicallyProximateFixture, LexicallyProximate_ReturnsRelativePathIfNotEmptyOrTestPath)
     {
         const auto& testParams = GetParam();
         AZ::IO::PathView testPath(testParams.m_testPathString, testParams.m_preferredSeparator);

+ 77 - 12
Code/Framework/AzFramework/AzFramework/Application/Application.cpp

@@ -77,11 +77,15 @@
 
 namespace AzFramework
 {
+
     namespace ApplicationInternal
     {
         static constexpr const char s_prefabSystemKey[] = "/Amazon/Preferences/EnablePrefabSystem";
         static constexpr const char s_prefabWipSystemKey[] = "/Amazon/Preferences/EnablePrefabSystemWipFeatures";
         static constexpr const char s_legacySlicesAssertKey[] = "/Amazon/Preferences/ShouldAssertForLegacySlicesUsage";
+        static constexpr const char* DeprecatedFileIOAliasesRoot = "/O3DE/AzCore/FileIO/DeprecatedAliases";
+        static constexpr const char* DeprecatedFileIOAliasesOldAliasKey = "OldAlias";
+        static constexpr const char* DeprecatedFileIOAliasesNewAliasKey = "NewAlias";
     }
 
     Application::Application()
@@ -563,6 +567,68 @@ namespace AzFramework
         }
     }
 
+    struct DeprecatedAliasesKeyVisitor
+        : AZ::SettingsRegistryInterface::Visitor
+    {
+        using VisitResponse = AZ::SettingsRegistryInterface::VisitResponse;
+        using VisitAction = AZ::SettingsRegistryInterface::VisitAction;
+        using Type = AZ::SettingsRegistryInterface::Type;
+
+        using AZ::SettingsRegistryInterface::Visitor::Visit;
+
+        VisitResponse Traverse(AZStd::string_view path, AZStd::string_view,
+            VisitAction action, Type type) override
+        {
+            if (action == AZ::SettingsRegistryInterface::VisitAction::Begin)
+            {
+                if (type == AZ::SettingsRegistryInterface::Type::Array)
+                {
+                    m_parentArrayPath = path;
+                }
+
+                // Strip off last path segment from json path and check if is a child element of the array
+                if (AZ::StringFunc::TokenizeLast(path, '/');
+                    m_parentArrayPath == path)
+                {
+                    m_aliases.emplace_back();
+                }
+            }
+            else if (action == AZ::SettingsRegistryInterface::VisitAction::End)
+            {
+                if (type == AZ::SettingsRegistryInterface::Type::Array)
+                {
+                    m_parentArrayPath = AZStd::string{};
+                }
+            }
+
+            return AZ::SettingsRegistryInterface::VisitResponse::Continue;
+        }
+
+        void Visit(AZStd::string_view, AZStd::string_view valueName, Type, AZStd::string_view value) override
+        {
+            if (!m_aliases.empty())
+            {
+                if (valueName == ApplicationInternal::DeprecatedFileIOAliasesOldAliasKey)
+                {
+                    m_aliases.back().m_oldAlias = value;
+                }
+                else if (valueName == ApplicationInternal::DeprecatedFileIOAliasesNewAliasKey)
+                {
+                    m_aliases.back().m_newAlias = value;
+                }
+            }
+        }
+
+        struct AliasPair
+        {
+            AZStd::string m_oldAlias;
+            AZStd::string m_newAlias;
+        };
+        AZStd::vector<AliasPair> m_aliases;
+
+    private:
+        AZStd::string m_parentArrayPath;
+    };
 
     static void CreateUserCache(const AZ::IO::FixedMaxPath& cacheUserPath, AZ::IO::FileIOBase& fileIoBase)
     {
@@ -610,9 +676,8 @@ namespace AzFramework
 
     void Application::SetFileIOAliases()
     {
-        if (m_archiveFileIO)
+        if (auto fileIoBase = m_archiveFileIO.get(); fileIoBase)
         {
-            auto fileIoBase = m_archiveFileIO.get();
             // Set up the default file aliases based on the settings registry
             fileIoBase->SetAlias("@engroot@", GetEngineRoot());
             fileIoBase->SetAlias("@projectroot@", GetEngineRoot());
@@ -620,29 +685,20 @@ namespace AzFramework
 
             {
                 AZ::IO::FixedMaxPath pathAliases;
-                if (m_settingsRegistry->Get(pathAliases.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_CacheProjectRootFolder))
-                {
-                    fileIoBase->SetAlias("@projectcache@", pathAliases.c_str());
-                }
                 pathAliases.clear();
                 if (m_settingsRegistry->Get(pathAliases.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_CacheRootFolder))
                 {
-                    fileIoBase->SetAlias("@assets@", pathAliases.c_str());
-                    fileIoBase->SetAlias("@projectplatformcache@", pathAliases.c_str());
-                    fileIoBase->SetAlias("@root@", pathAliases.c_str()); // Deprecated Use @projectplatformcache@
+                    fileIoBase->SetAlias("@products@", pathAliases.c_str());
                 }
                 pathAliases.clear();
                 if (m_settingsRegistry->Get(pathAliases.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder))
                 {
                     fileIoBase->SetAlias("@engroot@", pathAliases.c_str());
-                    fileIoBase->SetAlias("@devroot@", pathAliases.c_str()); // Deprecated - Use @engroot@
                 }
                 pathAliases.clear();
                 if (m_settingsRegistry->Get(pathAliases.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_ProjectPath))
                 {
-                    fileIoBase->SetAlias("@devassets@", pathAliases.c_str()); // Deprecated - Use @projectsourceassets@
                     fileIoBase->SetAlias("@projectroot@", pathAliases.c_str());
-                    fileIoBase->SetAlias("@projectsourceassets@", (pathAliases / "Assets").c_str());
                 }
             }
 
@@ -663,6 +719,15 @@ namespace AzFramework
             }
             fileIoBase->SetAlias("@log@", projectLogPath.c_str());
             fileIoBase->CreatePath(projectLogPath.c_str());
+
+            DeprecatedAliasesKeyVisitor visitor;
+            if (m_settingsRegistry->Visit(visitor, ApplicationInternal::DeprecatedFileIOAliasesRoot))
+            {
+                for (const auto& [oldAlias, newAlias] : visitor.m_aliases)
+                {
+                    fileIoBase->SetDeprecatedAlias(oldAlias, newAlias);
+                }
+            }
         }
     }
 

+ 3 - 3
Code/Framework/AzFramework/AzFramework/Archive/Archive.cpp

@@ -1121,7 +1121,7 @@ namespace AZ::IO
 
         if (AZ::IO::FixedMaxPath pathBindRoot; !AZ::IO::FileIOBase::GetDirectInstance()->ResolvePath(pathBindRoot, szBindRoot))
         {
-            AZ::IO::FileIOBase::GetDirectInstance()->ResolvePath(pathBindRoot, "@assets@");
+            AZ::IO::FileIOBase::GetDirectInstance()->ResolvePath(pathBindRoot, "@products@");
             desc.m_pathBindRoot = pathBindRoot.LexicallyNormal().String();
         }
         else
@@ -1807,9 +1807,9 @@ namespace AZ::IO
         if (m_eRecordFileOpenList != IArchive::RFOM_Disabled)
         {
             // we only want to record ASSET access
-            // assets are identified as files that are relative to the resolved @assets@ alias path
+            // assets are identified as files that are relative to the resolved @products@ alias path
             auto fileIoBase = AZ::IO::FileIOBase::GetInstance();
-            const char* aliasValue = fileIoBase->GetAlias("@assets@");
+            const char* aliasValue = fileIoBase->GetAlias("@products@");
 
             if (AZ::IO::FixedMaxPath resolvedFilePath;
                 fileIoBase->ResolvePath(resolvedFilePath, szFilename)

+ 10 - 0
Code/Framework/AzFramework/AzFramework/Archive/ArchiveFileIO.cpp

@@ -546,6 +546,16 @@ namespace AZ::IO
         realUnderlyingFileIO->GetAlias(alias);
     }
 
+    void ArchiveFileIO::SetDeprecatedAlias(AZStd::string_view oldAlias, AZStd::string_view newAlias)
+    {
+        FileIOBase* realUnderlyingFileIO = FileIOBase::GetDirectInstance();
+        if (!realUnderlyingFileIO)
+        {
+            return;
+        }
+        realUnderlyingFileIO->SetDeprecatedAlias(oldAlias, newAlias);
+    }
+
     AZStd::optional<AZ::u64> ArchiveFileIO::ConvertToAlias(char* inOutBuffer, AZ::u64 bufferLength) const
     {
         if ((!inOutBuffer) || (bufferLength == 0))

+ 1 - 0
Code/Framework/AzFramework/AzFramework/Archive/ArchiveFileIO.h

@@ -63,6 +63,7 @@ namespace AZ::IO
         IO::Result FindFiles(const char* filePath, const char* filter, FindFilesCallbackType callback) override;
         void SetAlias(const char* alias, const char* path) override;
         void ClearAlias(const char* alias) override;
+        void SetDeprecatedAlias(AZStd::string_view oldAlias, AZStd::string_view newAlias) override;
         AZStd::optional<AZ::u64> ConvertToAlias(char* inOutBuffer, AZ::u64 bufferLength) const override;
         bool ConvertToAlias(AZ::IO::FixedMaxPath& convertedPath, const AZ::IO::PathView& path) const override;
         using FileIOBase::ConvertToAlias;

+ 9 - 9
Code/Framework/AzFramework/AzFramework/Archive/ArchiveFindData.cpp

@@ -186,8 +186,8 @@ namespace AZ::IO
         {
             // filter out the stuff which does not match.
 
-            // the problem here is that szDir might be something like "@assets@/levels/*"
-            // but our archive might be mounted at the root, or at some other folder at like "@assets@" or "@assets@/levels/mylevel"
+            // the problem here is that szDir might be something like "@products@/levels/*"
+            // but our archive might be mounted at the root, or at some other folder at like "@products@" or "@products@/levels/mylevel"
             // so there's really no way to filter out opening the pack and looking at the files inside.
             // however, the bind root is not part of the inner zip entry name either
             // and the ZipDir::FindFile actually expects just the chopped off piece.
@@ -202,22 +202,22 @@ namespace AZ::IO
 
 
             // Example:
-            // "@assets@\\levels\\*" <--- szDir
-            // "@assets@\\" <--- mount point
+            // "@products@\\levels\\*" <--- szDir
+            // "@products@\\" <--- mount point
             // ~~~~~~~~~~~ Common part
             // "levels\\*" <---- remainder that is not in common
             // "" <--- mount point remainder.  In this case, we should scan the contents of the pak for the remainder
 
             // Example:
-            // "@assets@\\levels\\*" <--- szDir
-            // "@assets@\\levels\\mylevel\\" <--- mount point (its level.pak)
+            // "@products@\\levels\\*" <--- szDir
+            // "@products@\\levels\\mylevel\\" <--- mount point (its level.pak)
             //  ~~~~~~~~~~~~~~~~~~ common part
             // "*" <----  remainder that is not in common
             // "mylevel\\" <--- mount point remainder.
 
             // example:
-            // "@assets@\\levels\\otherlevel\\*" <--- szDir
-            // "@assets@\\levels\\mylevel\\" <--- mount point (its level.pak)
+            // "@products@\\levels\\otherlevel\\*" <--- szDir
+            // "@products@\\levels\\mylevel\\" <--- mount point (its level.pak)
             // "otherlevel\\*" <----  remainder
             // "mylevel\\" <--- mount point remainder.
 
@@ -249,7 +249,7 @@ namespace AZ::IO
                 // which means we may search inside the pack.
                 ScanInZip(it->pZip.get(), sourcePathRemainder.Native());
             }
-          
+
         }
     }
 

+ 1 - 1
Code/Framework/AzFramework/AzFramework/Archive/MissingFileReport.cpp

@@ -94,7 +94,7 @@ namespace AZ::IO::Internal
         }
 
         AZStd::smatch matches;
-        const AZStd::regex lodRegex("@assets@\\\\(.*)_lod[0-9]+(\\.cgfm?)");
+        const AZStd::regex lodRegex("@products@\\\\(.*)_lod[0-9]+(\\.cgfm?)");
         if (!AZStd::regex_match(szPath, matches, lodRegex) || matches.size() != 3)
         {
             // The current file is not a valid LOD file

+ 1 - 1
Code/Framework/AzFramework/AzFramework/Asset/AssetCatalog.cpp

@@ -725,7 +725,7 @@ namespace AzFramework
 
             if (!info.m_relativePath.empty())
             {
-                const char* devAssetRoot = fileIO->GetAlias("@devassets@");
+                const char* devAssetRoot = fileIO->GetAlias("@projectroot@");
                 if (devAssetRoot)
                 {
                     AZ::Data::AssetStreamInfo streamInfo;

+ 1 - 1
Code/Framework/AzFramework/AzFramework/Gem/GemInfo.cpp

@@ -61,7 +61,7 @@ namespace AzFramework
 
                     AZ::IO::Path& gemAbsPath = gemInfo.m_absoluteSourcePaths.emplace_back(value);
                     // Resolve any file aliases first - Do not use ResolvePath() as that assumes
-                    // any relative path is underneath the @assets@ alias
+                    // any relative path is underneath the @products@ alias
                     if (auto fileIoBase = AZ::IO::FileIOBase::GetInstance(); fileIoBase != nullptr)
                     {
                         AZ::IO::FixedMaxPath replacedAliasPath;

+ 89 - 30
Code/Framework/AzFramework/AzFramework/IO/LocalFileIO.cpp

@@ -12,10 +12,12 @@
 #include <AzCore/IO/Path/Path.h>
 #include <AzCore/Casting/numeric_cast.h>
 #include <AzCore/Casting/lossy_cast.h>
+#include <AzCore/std/containers/fixed_unordered_set.h>
 #include <AzCore/std/functional.h>
 #include <AzCore/std/string/conversions.h>
 #include <AzCore/std/string/string_view.h>
 #include <AzCore/StringFunc/StringFunc.h>
+#include <AzCore/Utils/Utils.h>
 #include <cctype>
 
 namespace AZ
@@ -292,7 +294,7 @@ namespace AZ
         void LocalFileIO::CheckInvalidWrite([[maybe_unused]] const char* path)
         {
 #if defined(AZ_ENABLE_TRACING)
-            const char* assetAliasPath = GetAlias("@assets@");
+            const char* assetAliasPath = GetAlias("@products@");
             if (path && assetAliasPath)
             {
                 const AZ::IO::PathView pathView(path);
@@ -478,17 +480,15 @@ namespace AZ
                 return false;
             }
 
-            if (IsAbsolutePath(path))
+            if (AZ::IO::PathView(path).HasRootPath())
             {
                 size_t pathLen = strlen(path);
                 if (pathLen + 1 < resolvedPathSize)
                 {
                     azstrncpy(resolvedPath, resolvedPathSize, path, pathLen + 1);
 
-                    //see if the absolute path uses @assets@ or @root@, if it does lowercase the relative part
-                    [[maybe_unused]] bool lowercasePath = LowerIfBeginsWith(resolvedPath, resolvedPathSize, GetAlias("@assets@"))
-                        || LowerIfBeginsWith(resolvedPath, resolvedPathSize, GetAlias("@root@"))
-                        || LowerIfBeginsWith(resolvedPath, resolvedPathSize, GetAlias("@projectplatformcache@"));
+                    //see if the absolute path matches the resolved value of @products@, if it does lowercase the relative part
+                    LowerIfBeginsWith(resolvedPath, resolvedPathSize, GetAlias("@products@"));
 
                     ToUnixSlashes(resolvedPath, resolvedPathSize);
                     return true;
@@ -499,34 +499,39 @@ namespace AZ
                 }
             }
 
-            char rootedPathBuffer[AZ_MAX_PATH_LEN] = {0};
+            constexpr AZStd::string_view productAssetAlias = "@products@";
+            // Add plus one for the path separator: <alias>/<path>
+            constexpr size_t MaxPathSizeWithProductAssetAlias = AZ::IO::MaxPathLength + productAssetAlias.size() + 1;
+            using RootedPathString = AZStd::fixed_string<MaxPathSizeWithProductAssetAlias>;
+            RootedPathString rootedPathBuffer;
             const char* rootedPath = path;
-            // if the path does not begin with an alias, then it is assumed to begin with @assets@
+            // if the path does not begin with an alias, then it is assumed to begin with @products@
             if (path[0] != '@')
             {
-                if (GetAlias("@assets@"))
+                if (GetAlias("@products@"))
                 {
-                    const int rootLength = 9;// strlen("@assets@/")
-                    azstrncpy(rootedPathBuffer, AZ_MAX_PATH_LEN, "@assets@/", rootLength);
-                    size_t pathLen = strlen(path);
-                    size_t rootedPathBufferlength = rootLength + pathLen + 1;// +1 for null terminator
-                    if (rootedPathBufferlength > resolvedPathSize)
+
+                    if (const size_t requiredSize = productAssetAlias.size() + strlen(path) + 1;
+                        requiredSize > rootedPathBuffer.capacity())
                     {
-                        AZ_Assert(rootedPathBufferlength < resolvedPathSize, "Constructed path length is wrong:%s", rootedPathBuffer);//path constructed is wrong
-                        size_t remainingSize = resolvedPathSize - rootLength - 1;// - 1 for null terminator
-                        azstrncpy(rootedPathBuffer + rootLength, AZ_MAX_PATH_LEN, path, remainingSize);
-                        rootedPathBuffer[resolvedPathSize - 1] = '\0';
+                        AZ_Error("FileIO", false, "Prepending the %.*s alias to the input path results in a path longer than the"
+                            " AZ::IO::MaxPathLength + the alias size of %zu. The size of the potential failed path is %zu",
+                            AZ_STRING_ARG(productAssetAlias), rootedPathBuffer.capacity(), requiredSize)
                     }
                     else
                     {
-                        azstrncpy(rootedPathBuffer + rootLength, AZ_MAX_PATH_LEN - rootLength, path, pathLen + 1);
+                        rootedPathBuffer = RootedPathString::format("%.*s/%s", AZ_STRING_ARG(productAssetAlias), path);
                     }
                 }
                 else
                 {
-                    ConvertToAbsolutePath(path, rootedPathBuffer, AZ_MAX_PATH_LEN);
+                    if (ConvertToAbsolutePath(path, rootedPathBuffer.data(), rootedPathBuffer.capacity()))
+                    {
+                        // Recalculate the internal string length
+                        rootedPathBuffer.resize_no_construct(AZStd::char_traits<char>::length(rootedPathBuffer.data()));
+                    }
                 }
-                rootedPath = rootedPathBuffer;
+                rootedPath = rootedPathBuffer.c_str();
             }
 
             if (ResolveAliases(rootedPath, resolvedPath, resolvedPathSize))
@@ -561,11 +566,57 @@ namespace AZ
 
         const char* LocalFileIO::GetAlias(const char* key) const
         {
-            const auto it = m_aliases.find(key);
-            if (it != m_aliases.end())
+            if (const auto it = m_aliases.find(key); it != m_aliases.end())
             {
                 return it->second.c_str();
             }
+            else if (const auto deprecatedIt = m_deprecatedAliases.find(key);
+                deprecatedIt != m_deprecatedAliases.end())
+            {
+                AZ_Error("FileIO", false, R"(Alias "%s" is deprecated. Please use alias "%s" instead)",
+                    key, deprecatedIt->second.c_str());
+                AZStd::string_view aliasValue = deprecatedIt->second;
+                // Contains the list of aliases resolved so far
+                // If max_size is hit, than an error is logged and nullptr is returned
+                using VisitedAliasSet = AZStd::fixed_unordered_set<AZStd::string_view, 8, 8>;
+                VisitedAliasSet visitedAliasSet;
+                while (aliasValue.starts_with("@"))
+                {
+                    if (visitedAliasSet.contains(aliasValue))
+                    {
+                        AZ_Error("FileIO", false, "Cycle found with for alias %.*s when trying to resolve deprecated alias %s",
+                            AZ_STRING_ARG(aliasValue), key);
+                        return nullptr;
+                    }
+
+                    if(visitedAliasSet.size() == visitedAliasSet.max_size())
+                    {
+                        AZ_Error("FileIO", false, "Unable to resolve path to deprecated alias %s within %zu steps",
+                            key, visitedAliasSet.max_size());
+                        return nullptr;
+                    }
+
+                    // Add the current alias value to the visited set
+                    visitedAliasSet.emplace(aliasValue);
+
+                    // Check if the alias value corresponds to another alias
+                    if (auto resolvedIter = m_aliases.find(aliasValue); resolvedIter != m_aliases.end())
+                    {
+                        aliasValue = resolvedIter->second;
+                    }
+                    else if (resolvedIter = m_deprecatedAliases.find(aliasValue);
+                        resolvedIter != m_deprecatedAliases.end())
+                    {
+                        aliasValue = resolvedIter->second;
+                    }
+                    else
+                    {
+                        return nullptr;
+                    }
+                }
+
+                return aliasValue.data();
+            }
             return nullptr;
         }
 
@@ -574,6 +625,11 @@ namespace AZ
             m_aliases.erase(key);
         }
 
+        void LocalFileIO::SetDeprecatedAlias(AZStd::string_view oldAlias, AZStd::string_view newAlias)
+        {
+            m_deprecatedAliases[oldAlias] = newAlias;
+        }
+
         AZStd::optional<AZ::u64> LocalFileIO::ConvertToAliasBuffer(char* outBuffer, AZ::u64 outBufferLength, AZStd::string_view inBuffer) const
         {
             size_t longestMatch = 0;
@@ -675,7 +731,9 @@ namespace AZ
                                                                      : string_view_pair{};
 
             size_t requiredResolvedPathSize = pathView.size() - aliasKey.size() + aliasValue.size() + 1;
-            AZ_Assert(path != resolvedPath && resolvedPathSize >= requiredResolvedPathSize, "Resolved path is incorrect");
+            AZ_Assert(path != resolvedPath, "ResolveAliases does not support inplace update of the path");
+            AZ_Assert(resolvedPathSize >= requiredResolvedPathSize, "Resolved path size %llu not large enough. It needs to be %zu",
+                resolvedPathSize, requiredResolvedPathSize);
             // we assert above, but we also need to properly handle the case when the resolvedPath buffer size
             // is too small to copy the source into.
             if (path == resolvedPath || (resolvedPathSize < requiredResolvedPathSize))
@@ -699,13 +757,9 @@ namespace AZ
                 resolvedPath[resolvedPathLen] = '\0';
 
                 // If the path started with one of the "asset cache" path aliases, lowercase the path
-                const char* assetAliasPath = GetAlias("@assets@");
-                const char* rootAliasPath = GetAlias("@root@");
-                const char* projectPlatformCacheAliasPath = GetAlias("@projectplatformcache@");
+                const char* projectPlatformCacheAliasPath = GetAlias("@products@");
 
-                const bool lowercasePath = (assetAliasPath != nullptr && AZ::StringFunc::StartsWith(resolvedPath, assetAliasPath)) ||
-                    (rootAliasPath != nullptr && AZ::StringFunc::StartsWith(resolvedPath, rootAliasPath)) ||
-                    (projectPlatformCacheAliasPath != nullptr && AZ::StringFunc::StartsWith(resolvedPath, projectPlatformCacheAliasPath));
+                const bool lowercasePath = projectPlatformCacheAliasPath != nullptr && AZ::StringFunc::StartsWith(resolvedPath, projectPlatformCacheAliasPath);
 
                 if (lowercasePath)
                 {
@@ -822,5 +876,10 @@ namespace AZ
 
             return pathStr + "/";
         }
+
+        bool LocalFileIO::ConvertToAbsolutePath(const char* path, char* absolutePath, AZ::u64 maxLength) const
+        {
+            return AZ::Utils::ConvertToAbsolutePath(path, absolutePath, maxLength);
+        }
     } // namespace IO
 } // namespace AZ

+ 4 - 2
Code/Framework/AzFramework/AzFramework/IO/LocalFileIO.h

@@ -61,6 +61,8 @@ namespace AZ
             void SetAlias(const char* alias, const char* path) override;
             void ClearAlias(const char* alias) override;
             const char* GetAlias(const char* alias) const override;
+            void SetDeprecatedAlias(AZStd::string_view oldAlias, AZStd::string_view newAlias) override;
+
             AZStd::optional<AZ::u64> ConvertToAlias(char* inOutBuffer, AZ::u64 bufferLength) const override;
             bool ConvertToAlias(AZ::IO::FixedMaxPath& convertedPath, const AZ::IO::PathView& path) const override;
             using FileIOBase::ConvertToAlias;
@@ -71,7 +73,7 @@ namespace AZ
 
             bool GetFilename(HandleType fileHandle, char* filename, AZ::u64 filenameSize) const override;
             bool ConvertToAbsolutePath(const char* path, char* absolutePath, AZ::u64 maxLength) const;
-            
+
         private:
             SystemFile* GetFilePointerFromHandle(HandleType fileHandle);
 
@@ -79,7 +81,6 @@ namespace AZ
 
             AZStd::optional<AZ::u64> ConvertToAliasBuffer(char* outBuffer, AZ::u64 outBufferLength, AZStd::string_view inBuffer) const;
             bool ResolveAliases(const char* path, char* resolvedPath, AZ::u64 resolvedPathSize) const;
-            bool IsAbsolutePath(const char* path) const;
 
             bool LowerIfBeginsWith(char* inOutBuffer, AZ::u64 bufferLen, const char* alias) const;
 
@@ -91,6 +92,7 @@ namespace AZ
             AZStd::atomic<HandleType> m_nextHandle;
             AZStd::unordered_map<HandleType, SystemFile> m_openFiles;
             AZStd::unordered_map<AZStd::string, AZStd::string> m_aliases;
+            AZStd::unordered_map<AZStd::string, AZStd::string> m_deprecatedAliases;
 
             void CheckInvalidWrite(const char* path);
         };

+ 22 - 8
Code/Framework/AzFramework/AzFramework/IO/RemoteFileIO.cpp

@@ -49,14 +49,14 @@ namespace AZ
                 s_IOLog.append(m_name);
                 s_IOLog.append("\r\n");
             }
-            
+
             void Append(const char* line)
             {
                 s_IOLog.append(AZStd::string::format("%u ", m_fileOperation));
                 s_IOLog.append(line);
                 s_IOLog.append("\r\n");
             }
-            
+
             ~LogCall()
             {
                 s_IOLog.append(AZStd::string::format("%u End ", m_fileOperation));
@@ -251,7 +251,7 @@ namespace AZ
                 REMOTEFILE_LOG_APPEND(AZStd::string::format("NetworkFileIO::Size(filePath=%s) size request failed. return Error", filePath).c_str());
                 return ResultCode::Error;
             }
-            
+
             size = response.m_size;
             REMOTEFILE_LOG_APPEND(AZStd::string::format("NetworkFileIO::Size(filePath=%s) size=%u. return Success", filePath, size).c_str());
             return ResultCode::Success;
@@ -793,6 +793,12 @@ namespace AZ
             REMOTEFILE_LOG_CALL(AZStd::string::format("NetworkFileIO()::ClearAlias(alias=%s)", alias?alias:"nullptr").c_str());
         }
 
+        void NetworkFileIO::SetDeprecatedAlias([[maybe_unused]] AZStd::string_view oldAlias, [[maybe_unused]] AZStd::string_view newAlias)
+        {
+            REMOTEFILE_LOG_CALL(AZStd::string::format("NetworkFileIO()::SetDeprecatedAlias(oldAlias=%.*s, newAlias=%.*s)",
+                AZ_STRING_ARG(oldAlias), AZ_STRING_ARG(newAlias)).c_str());
+        }
+
         AZStd::optional<AZ::u64> NetworkFileIO::ConvertToAlias(char* inOutBuffer, [[maybe_unused]] AZ::u64 bufferLength) const
         {
             REMOTEFILE_LOG_CALL(AZStd::string::format("NetworkFileIO()::ConvertToAlias(inOutBuffer=%s, bufferLength=%u)", inOutBuffer?inOutBuffer:"nullptr", bufferLength).c_str());
@@ -927,7 +933,7 @@ namespace AZ
         {
             m_cacheLookaheadPos = filePosition - CacheStartFilePosition();
         }
-        
+
         void RemoteFileCache::SyncCheck()
         {
 #ifdef REMOTEFILEIO_SYNC_CHECK
@@ -955,7 +961,7 @@ namespace AZ
                 AZ_TracePrintf(RemoteFileCacheChannel, "RemoteFileCache::SyncCheck(m_fileHandle=%u) tell request failed.", m_fileHandle);
                 REMOTEFILE_LOG_APPEND(AZStd::string::format("RemoteFileCache::SyncCheck(m_fileHandle=%u) tell request failed.", m_fileHandle).c_str());
             }
-            
+
             if (responce.m_offset != m_filePosition)
             {
                 AZ_TracePrintf(RemoteFileCacheChannel, "RemoteFileCache::SyncCheck(m_fileHandle=%u) failed!!! m_filePosition=%u tell=%u", m_fileHandle, m_filePosition, responce.m_offset);
@@ -1028,7 +1034,7 @@ namespace AZ
         {
             REMOTEFILE_LOG_CALL(AZStd::string::format("RemoteFileIO()::Close(fileHandle=%u)", fileHandle).c_str());
             Result returnValue = NetworkFileIO::Close(fileHandle);
-            
+
             if (returnValue == ResultCode::Success)
             {
                 AZStd::lock_guard<AZStd::recursive_mutex> lock(m_remoteFileCacheGuard);
@@ -1160,7 +1166,7 @@ namespace AZ
             REMOTEFILE_LOG_CALL(AZStd::string::format("RemoteFileIO()::Read(fileHandle=%u, buffer=OUT, size=%u, failOnFewerThanSizeBytesRead=%s, bytesRead=OUT)", fileHandle, size, failOnFewerThanSizeBytesRead ? "True" : "False").c_str());
             AZStd::lock_guard<AZStd::recursive_mutex> lock(m_remoteFileCacheGuard);
             RemoteFileCache& cache = GetCache(fileHandle);
-            
+
             AZ::u64 remainingBytesToRead = size;
             AZ::u64 bytesReadFromCache = 0;
             AZ::u64 remainingBytesInCache = cache.RemainingBytes();
@@ -1263,7 +1269,7 @@ namespace AZ
             RemoteFileCache& cache = GetCache(fileHandle);
             if (cache.m_cacheLookaheadBuffer.size() && cache.RemainingBytes())
             {
-                // find out where we are 
+                // find out where we are
                 AZ::u64 seekPosition = cache.CacheFilePosition();
 
                 // note, seeks are predicted, and do not ask for a response.
@@ -1361,6 +1367,14 @@ namespace AZ
             }
         }
 
+        void RemoteFileIO::SetDeprecatedAlias(AZStd::string_view oldAlias, AZStd::string_view newAlias)
+        {
+            if (m_excludedFileIO)
+            {
+                m_excludedFileIO->SetDeprecatedAlias(oldAlias, newAlias);
+            }
+        }
+
         AZStd::optional<AZ::u64> RemoteFileIO::ConvertToAlias(char* inOutBuffer, AZ::u64 bufferLength) const
         {
             return m_excludedFileIO ? m_excludedFileIO->ConvertToAlias(inOutBuffer, bufferLength) : strlen(inOutBuffer);

+ 2 - 0
Code/Framework/AzFramework/AzFramework/IO/RemoteFileIO.h

@@ -102,6 +102,7 @@ namespace AZ
             Result FindFiles(const char* filePath, const char* filter, FindFilesCallbackType callback) override;
             void SetAlias(const char* alias, const char* path) override;
             void ClearAlias(const char* alias) override;
+            void SetDeprecatedAlias(AZStd::string_view oldAlias, AZStd::string_view newAlias) override;
             AZStd::optional<AZ::u64> ConvertToAlias(char* inOutBuffer, AZ::u64 bufferLength) const override;
             bool ConvertToAlias(AZ::IO::FixedMaxPath& convertedPath, const AZ::IO::PathView& path) const override;
             using FileIOBase::ConvertToAlias;
@@ -194,6 +195,7 @@ namespace AZ
             void SetAlias(const char* alias, const char* path) override;
             const char* GetAlias(const char* alias) const override;
             void ClearAlias(const char* alias) override;
+            void SetDeprecatedAlias(AZStd::string_view oldAlias, AZStd::string_view newAlias) override;
             AZStd::optional<AZ::u64> ConvertToAlias(char* inOutBuffer, AZ::u64 bufferLength) const override;
             bool ConvertToAlias(AZ::IO::FixedMaxPath& convertedPath, const AZ::IO::PathView& path) const override;
             using FileIOBase::ConvertToAlias;

+ 13 - 49
Code/Framework/AzFramework/Platform/Android/AzFramework/IO/LocalFileIO_Android.cpp

@@ -13,7 +13,6 @@
 #include <AzCore/Android/Utils.h>
 #include <AzCore/IO/IOUtils.h>
 #include <AzCore/IO/Path/Path.h>
-#include <AzCore/IO/SystemFile.h>
 #include <AzCore/std/functional.h>
 
 #include <android/api-level.h>
@@ -42,10 +41,10 @@ namespace AZ
     {
         Result LocalFileIO::Copy(const char* sourceFilePath, const char* destinationFilePath)
         {
-            char resolvedSourcePath[AZ_MAX_PATH_LEN];
-            char resolvedDestPath[AZ_MAX_PATH_LEN];
-            ResolvePath(sourceFilePath, resolvedSourcePath, AZ_MAX_PATH_LEN);
-            ResolvePath(destinationFilePath, resolvedDestPath, AZ_MAX_PATH_LEN);
+            char resolvedSourcePath[AZ::IO::MaxPathLength];
+            char resolvedDestPath[AZ::IO::MaxPathLength];
+            ResolvePath(sourceFilePath, resolvedSourcePath, AZ::IO::MaxPathLength);
+            ResolvePath(destinationFilePath, resolvedDestPath, AZ::IO::MaxPathLength);
 
             if (AZ::Android::Utils::IsApkPath(sourceFilePath) || AZ::Android::Utils::IsApkPath(destinationFilePath))
             {
@@ -77,18 +76,17 @@ namespace AZ
         {
             ANDROID_IO_PROFILE_SECTION_ARGS("FindFiles:%s", filePath);
 
-            char resolvedPath[AZ_MAX_PATH_LEN];
-            ResolvePath(filePath, resolvedPath, AZ_MAX_PATH_LEN);
+            char resolvedPath[AZ::IO::MaxPathLength];
+            ResolvePath(filePath, resolvedPath, AZ::IO::MaxPathLength);
 
             AZStd::string pathWithoutSlash = RemoveTrailingSlash(resolvedPath);
             bool isInAPK = AZ::Android::Utils::IsApkPath(pathWithoutSlash.c_str());
 
+            AZ::IO::FixedMaxPath tempBuffer;
             if (isInAPK)
             {
                 AZ::IO::FixedMaxPath strippedPath = AZ::Android::Utils::StripApkPrefix(pathWithoutSlash.c_str());
 
-                char tempBuffer[AZ_MAX_PATH_LEN] = {0};
-
                 AZ::Android::APKFileHandler::ParseDirectory(strippedPath.c_str(), [&](const char* name)
                     {
                         AZStd::string_view filenameView = name;
@@ -98,10 +96,9 @@ namespace AZ
                             AZStd::string foundFilePath = CheckForTrailingSlash(resolvedPath);
                             foundFilePath += name;
                             // if aliased, de-alias!
-                            azstrcpy(tempBuffer, AZ_MAX_PATH_LEN, foundFilePath.c_str());
-                            ConvertToAlias(tempBuffer, AZ_MAX_PATH_LEN);
+                            ConvertToAlias(tempBuffer, AZ::IO::PathView{ foundFilePath });
 
-                            if (!callback(tempBuffer))
+                            if (!callback(tempBuffer.c_str()))
                             {
                                 return false;
                             }
@@ -115,10 +112,6 @@ namespace AZ
 
                 if (dir != nullptr)
                 {
-                    // because the absolute path might actually be SHORTER than the alias ("c:/r/dev" -> "@devroot@"), we need to
-                    // use a static buffer here.
-                    char tempBuffer[AZ_MAX_PATH_LEN];
-
                     // clear the errno state so we can distinguish between errors and end of stream
                     errno = 0;
                     struct dirent* entry = readdir(dir);
@@ -133,10 +126,9 @@ namespace AZ
                             AZStd::string foundFilePath = CheckForTrailingSlash(resolvedPath);
                             foundFilePath += entry->d_name;
                             // if aliased, de-alias!
-                            azstrcpy(tempBuffer, AZ_MAX_PATH_LEN, foundFilePath.c_str());
-                            ConvertToAlias(tempBuffer, AZ_MAX_PATH_LEN);
+                            ConvertToAlias(tempBuffer, AZ::IO::PathView{ foundFilePath });
 
-                            if (!callback(tempBuffer))
+                            if (!callback(tempBuffer.c_str()))
                             {
                                 break;
                             }
@@ -163,8 +155,8 @@ namespace AZ
 
         Result LocalFileIO::CreatePath(const char* filePath)
         {
-            char resolvedPath[AZ_MAX_PATH_LEN];
-            ResolvePath(filePath, resolvedPath, AZ_MAX_PATH_LEN);
+            char resolvedPath[AZ::IO::MaxPathLength];
+            ResolvePath(filePath, resolvedPath, AZ::IO::MaxPathLength);
 
             if (AZ::Android::Utils::IsApkPath(resolvedPath))
             {
@@ -201,33 +193,5 @@ namespace AZ
             mkdir(pathBuffer.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
             return IsDirectory(resolvedPath) ? ResultCode::Success : ResultCode::Error;
         }
-
-        bool LocalFileIO::IsAbsolutePath(const char* path) const
-        {
-            return path && path[0] == '/';
-        }
-
-        bool LocalFileIO::ConvertToAbsolutePath(const char* path, char* absolutePath, AZ::u64 maxLength) const
-        {
-            if (AZ::Android::Utils::IsApkPath(path))
-            {
-                azstrncpy(absolutePath, maxLength, path, maxLength);
-                return true;
-            }
-            AZ_Assert(maxLength >= AZ_MAX_PATH_LEN, "Path length is larger than AZ_MAX_PATH_LEN");
-            if (!IsAbsolutePath(path))
-            {
-                // note that realpath fails if the path does not exist and actually changes the return value
-                // to be the actual place that FAILED, which we don't want.
-                // if we fail, we'd prefer to fall through and at least use the original path.
-                const char* result = realpath(path, absolutePath);
-                if (result)
-                {
-                    return true;
-                }
-            }
-            azstrcpy(absolutePath, maxLength, path);
-            return IsAbsolutePath(absolutePath);
-        }
     } // namespace IO
 }//namespace AZ

+ 12 - 38
Code/Framework/AzFramework/Platform/Common/UnixLike/AzFramework/IO/LocalFileIO_UnixLike.cpp

@@ -10,7 +10,7 @@
 #include <dirent.h>
 #include <unistd.h>
 #include <AzFramework/IO/LocalFileIO.h>
-#include <AzCore/IO/SystemFile.h>
+#include <AzCore/IO/Path/Path.h>
 #include <AzCore/std/functional.h>
 
 namespace AZ
@@ -19,11 +19,11 @@ namespace AZ
     {
         Result LocalFileIO::Copy(const char* sourceFilePath, const char* destinationFilePath)
         {
-            char resolvedSourceFilePath[AZ_MAX_PATH_LEN] = {0};
-            ResolvePath(sourceFilePath, resolvedSourceFilePath, AZ_MAX_PATH_LEN);
+            char resolvedSourceFilePath[AZ::IO::MaxPathLength] = {0};
+            ResolvePath(sourceFilePath, resolvedSourceFilePath, AZ::IO::MaxPathLength);
 
-            char resolvedDestinationFilePath[AZ_MAX_PATH_LEN] = {0};
-            ResolvePath(destinationFilePath, resolvedDestinationFilePath, AZ_MAX_PATH_LEN);
+            char resolvedDestinationFilePath[AZ::IO::MaxPathLength] = {0};
+            ResolvePath(destinationFilePath, resolvedDestinationFilePath, AZ::IO::MaxPathLength);
 
             // Use standard C++ method of file copy.
             {
@@ -45,17 +45,15 @@ namespace AZ
 
         Result LocalFileIO::FindFiles(const char* filePath, const char* filter, FindFilesCallbackType callback)
         {
-            char resolvedPath[AZ_MAX_PATH_LEN] = {0};
-            ResolvePath(filePath, resolvedPath, AZ_MAX_PATH_LEN);
+            char resolvedPath[AZ::IO::MaxPathLength] = {0};
+            ResolvePath(filePath, resolvedPath, AZ::IO::MaxPathLength);
 
             AZStd::string withoutSlash = RemoveTrailingSlash(resolvedPath);
             DIR* dir = opendir(withoutSlash.c_str());
 
             if (dir != nullptr)
             {
-                // because the absolute path might actually be SHORTER than the alias ("c:/r/dev" -> "@devroot@"), we need to
-                // use a static buffer here.
-                char tempBuffer[AZ_MAX_PATH_LEN];
+                AZ::IO::FixedMaxPath tempBuffer;
 
                 errno = 0;
                 struct dirent* entry = readdir(dir);
@@ -70,10 +68,9 @@ namespace AZ
                         AZStd::string foundFilePath = CheckForTrailingSlash(resolvedPath);
                         foundFilePath += entry->d_name;
                         // if aliased, dealias!
-                        azstrcpy(tempBuffer, AZ_MAX_PATH_LEN, foundFilePath.c_str());
-                        ConvertToAlias(tempBuffer, AZ_MAX_PATH_LEN);
+                        ConvertToAlias(tempBuffer, AZ::IO::PathView{ foundFilePath });
 
-                        if (!callback(tempBuffer))
+                        if (!callback(tempBuffer.c_str()))
                         {
                             break;
                         }
@@ -92,8 +89,8 @@ namespace AZ
 
         Result LocalFileIO::CreatePath(const char* filePath)
         {
-            char resolvedPath[AZ_MAX_PATH_LEN] = {0};
-            ResolvePath(filePath, resolvedPath, AZ_MAX_PATH_LEN);
+            char resolvedPath[AZ::IO::MaxPathLength] = {0};
+            ResolvePath(filePath, resolvedPath, AZ::IO::MaxPathLength);
 
             // create all paths up to that directory.
             // its not an error if the path exists.
@@ -125,28 +122,5 @@ namespace AZ
             mkdir(buf.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
             return IsDirectory(resolvedPath) ? ResultCode::Success : ResultCode::Error;
         }
-
-        bool LocalFileIO::IsAbsolutePath(const char* path) const
-        {
-            return path && path[0] == '/';
-        }
-
-        bool LocalFileIO::ConvertToAbsolutePath(const char* path, char* absolutePath, AZ::u64 maxLength) const
-        {
-            AZ_Assert(maxLength >= AZ_MAX_PATH_LEN, "Path length is larger than AZ_MAX_PATH_LEN");
-            if (!IsAbsolutePath(path))
-            {
-                // note that realpath fails if the path does not exist and actually changes the return value
-                // to be the actual place that FAILED, which we don't want.
-                // if we fail, we'd prefer to fall through and at least use the original path.
-                const char* result = realpath(path, absolutePath);
-                if (result)
-                {
-                    return true;
-                }
-            }
-            azstrcpy(absolutePath, maxLength, path);
-            return IsAbsolutePath(absolutePath);
-        }
     } // namespace IO
 } // namespace AZ

+ 1 - 32
Code/Framework/AzFramework/Platform/Common/WinAPI/AzFramework/IO/LocalFileIO_WinAPI.cpp

@@ -47,7 +47,7 @@ namespace AZ
 
             if (hFind != INVALID_HANDLE_VALUE)
             {
-                // because the absolute path might actually be SHORTER than the alias ("c:/r/dev" -> "@devroot@"), we need to
+                // because the absolute path might actually be SHORTER than the alias ("D:/o3de" -> "@engroot@"), we need to
                 // use a static buffer here.
                 char tempBuffer[AZ_MAX_PATH_LEN];
                 do
@@ -133,36 +133,5 @@ namespace AZ
 
             return SystemFile::CreateDir(buf.c_str()) ? ResultCode::Success : ResultCode::Error;
         }
-
-        bool LocalFileIO::ConvertToAbsolutePath(const char* path, char* absolutePath, AZ::u64 maxLength) const
-        {
-            char* result = _fullpath(absolutePath, path, maxLength);
-            size_t len = ::strlen(absolutePath);
-            if (len > 0)
-            {
-                // strip trailing slash
-                if (absolutePath[len - 1] == '/' || absolutePath[len - 1] == '\\')
-                {
-                    absolutePath[len - 1] = 0;
-                }
-
-                // For some reason, at least on windows, _fullpath returns a lowercase drive letter even though other systems like Qt, use upper case.
-                if (len > 2)
-                {
-                    if (absolutePath[1] == ':')
-                    {
-                        absolutePath[0] = (char)toupper(absolutePath[0]);
-                    }
-                }
-            }
-            return result != nullptr;
-        }
-
-        bool LocalFileIO::IsAbsolutePath(const char* path) const
-        {
-            char drive[16] = { 0 };
-            _splitpath_s(path, drive, 16, nullptr, 0, nullptr, 0, nullptr, 0);
-            return strlen(drive) > 0;
-        }
     } // namespace IO
 }//namespace AZ

+ 15 - 17
Code/Framework/AzFramework/Platform/Windows/AzFramework/IO/LocalFileIO_Windows.cpp

@@ -7,26 +7,24 @@
  */
 #include <AzCore/PlatformIncl.h>
 #include <AzFramework/IO/LocalFileIO.h>
+#include <AzCore/std/string/conversions.h>
+#include <AzCore/IO/Path/Path.h>
 #include <AzCore/IO/SystemFile.h>
 
-namespace AZ
+namespace AZ::IO
 {
-    namespace IO
+    Result LocalFileIO::Copy(const char* sourceFilePath, const char* destinationFilePath)
     {
+        AZ::IO::FixedMaxPath resolvedSourcePath;
+        ResolvePath(resolvedSourcePath, sourceFilePath);
+        AZ::IO::FixedMaxPath resolvedDestPath;
+        ResolvePath(resolvedDestPath, destinationFilePath);
 
-        Result LocalFileIO::Copy(const char* sourceFilePath, const char* destinationFilePath)
-        {
-            char resolvedSourcePath[AZ_MAX_PATH_LEN];
-            ResolvePath(sourceFilePath, resolvedSourcePath, AZ_MAX_PATH_LEN);
-            char resolvedDestPath[AZ_MAX_PATH_LEN];
-            ResolvePath(destinationFilePath, resolvedDestPath, AZ_MAX_PATH_LEN);
+        AZStd::fixed_wstring<AZ::IO::MaxPathLength> resolvedSourcePathW;
+        AZStd::fixed_wstring<AZ::IO::MaxPathLength> resolvedDestPathW;
+        AZStd::to_wstring(resolvedSourcePathW, resolvedSourcePath.Native());
+        AZStd::to_wstring(resolvedDestPathW, resolvedDestPath.Native());
 
-            if (::CopyFileA(resolvedSourcePath, resolvedDestPath, false) == 0)
-            {
-                return ResultCode::Error;
-            }
-
-            return ResultCode::Success;
-        }
-    } // namespace IO
-}//namespace AZ
+        return ::CopyFileW(resolvedSourcePathW.c_str(), resolvedDestPathW.c_str(), false) != 0 ? ResultCode::Success : ResultCode::Error;
+    }
+}//namespace AZ::IO

+ 1 - 1
Code/Framework/AzFramework/Tests/Application.cpp

@@ -26,7 +26,7 @@ protected:
         }
         if (auto fileIoBase = AZ::IO::FileIOBase::GetInstance(); fileIoBase != nullptr)
         {
-            fileIoBase->SetAlias("@assets@", m_tempDirectory.GetDirectory());
+            fileIoBase->SetAlias("@products@", m_tempDirectory.GetDirectory());
         }
     }
 

+ 31 - 31
Code/Framework/AzFramework/Tests/ArchiveTests.cpp

@@ -50,7 +50,7 @@ namespace UnitTest
 
             m_application->Start({});
             // Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is
-            // shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash 
+            // shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash
             // in the unit tests.
             AZ::UserSettingsComponentRequestBus::Broadcast(&AZ::UserSettingsComponentRequests::DisableSaveOnFinalize);
         }
@@ -262,7 +262,7 @@ namespace UnitTest
             pArchive.reset();
             EXPECT_TRUE(IsPackValid(testArchivePath_withSubfolders.c_str()));
 
-            EXPECT_TRUE(archive->OpenPack("@assets@", testArchivePath_withSubfolders.c_str()));
+            EXPECT_TRUE(archive->OpenPack("@products@", testArchivePath_withSubfolders.c_str()));
 
             EXPECT_TRUE(archive->IsFileExist(fileInArchiveFile));
         }
@@ -353,7 +353,7 @@ namespace UnitTest
         // and be able to IMMEDIATELY
         // * read the file in the subfolder
         // * enumerate the folders (including that subfolder) even though they are 'virtual', not real folders on physical media
-        // * all of the above even though the mount point for the archive is @assets@ wheras the physical pack lives in @usercache@
+        // * all of the above even though the mount point for the archive is @products@ wheras the physical pack lives in @usercache@
         // finally, we're going to repeat the above test but with files mounted with subfolders
         // so for example, the pack will contain levelinfo.xml at the root of it
         // but it will be mounted at a subfolder (levels/mylevel).
@@ -388,7 +388,7 @@ namespace UnitTest
         pArchive.reset();
         EXPECT_TRUE(IsPackValid(testArchivePath_withSubfolders));
 
-        EXPECT_TRUE(archive->OpenPack("@assets@", testArchivePath_withSubfolders));
+        EXPECT_TRUE(archive->OpenPack("@products@", testArchivePath_withSubfolders));
         // ---- BARRAGE OF TESTS
         EXPECT_TRUE(archive->IsFileExist("levels\\mylevel\\levelinfo.xml"));
         EXPECT_TRUE(archive->IsFileExist("levels//mylevel//levelinfo.xml"));
@@ -484,7 +484,7 @@ namespace UnitTest
         pArchive.reset();
         EXPECT_TRUE(IsPackValid(testArchivePath_withMountPoint));
 
-        EXPECT_TRUE(archive->OpenPack("@assets@\\uniquename\\mylevel2", testArchivePath_withMountPoint));
+        EXPECT_TRUE(archive->OpenPack("@products@\\uniquename\\mylevel2", testArchivePath_withMountPoint));
 
         // ---- BARRAGE OF TESTS
         EXPECT_TRUE(archive->IsFileExist("uniquename\\mylevel2\\levelinfo.xml"));
@@ -543,7 +543,7 @@ namespace UnitTest
         archive->ClosePack(testArchivePath_withMountPoint);
 
         // --- test to make sure that when you iterate only the first component is found, so bury it deep and ask for the root
-        EXPECT_TRUE(archive->OpenPack("@assets@\\uniquename\\mylevel2\\mylevel3\\mylevel4", testArchivePath_withMountPoint));
+        EXPECT_TRUE(archive->OpenPack("@products@\\uniquename\\mylevel2\\mylevel3\\mylevel4", testArchivePath_withMountPoint));
 
         found_mylevel_folder = false;
         handle = archive->FindFirst("uniquename\\*");
@@ -574,9 +574,9 @@ namespace UnitTest
         found_mylevel_folder = false;
 
         // now make sure no red herrings appear
-        // for example, if a file is mounted at "@assets@\\uniquename\\mylevel2\\mylevel3\\mylevel4"
-        // and the file "@assets@\\somethingelse" is requested it should not be found
-        // in addition if the file "@assets@\\uniquename\\mylevel3" is requested it should not be found
+        // for example, if a file is mounted at "@products@\\uniquename\\mylevel2\\mylevel3\\mylevel4"
+        // and the file "@products@\\somethingelse" is requested it should not be found
+        // in addition if the file "@products@\\uniquename\\mylevel3" is requested it should not be found
         handle = archive->FindFirst("somethingelse\\*");
         EXPECT_FALSE(static_cast<bool>(handle));
 
@@ -610,7 +610,7 @@ namespace UnitTest
         cpfio.Remove(genericArchiveFileName);
 
         // create the asset alias directory
-        cpfio.CreatePath("@assets@");
+        cpfio.CreatePath("@products@");
 
         // create generic file
 
@@ -635,11 +635,11 @@ namespace UnitTest
         pArchive.reset();
         EXPECT_TRUE(IsPackValid(genericArchiveFileName));
 
-        EXPECT_TRUE(archive->OpenPack("@assets@", genericArchiveFileName));
+        EXPECT_TRUE(archive->OpenPack("@products@", genericArchiveFileName));
 
         // ---- BARRAGE OF TESTS
         EXPECT_TRUE(cpfio.Exists("testfile.xml"));
-        EXPECT_TRUE(cpfio.Exists("@assets@/testfile.xml"));  // this should be hte same file
+        EXPECT_TRUE(cpfio.Exists("@products@/testfile.xml"));  // this should be hte same file
         EXPECT_TRUE(!cpfio.Exists("@log@/testfile.xml"));
         EXPECT_TRUE(!cpfio.Exists("@usercache@/testfile.xml"));
         EXPECT_TRUE(cpfio.Exists("@log@/unittesttemp/realfileforunittest.xml"));
@@ -685,9 +685,9 @@ namespace UnitTest
         EXPECT_EQ(ResultCode::Success, cpfio.Close(normalFileHandle));
 
         EXPECT_TRUE(!cpfio.IsDirectory("testfile.xml"));
-        EXPECT_TRUE(cpfio.IsDirectory("@assets@"));
+        EXPECT_TRUE(cpfio.IsDirectory("@products@"));
         EXPECT_TRUE(cpfio.IsReadOnly("testfile.xml"));
-        EXPECT_TRUE(cpfio.IsReadOnly("@assets@/testfile.xml"));
+        EXPECT_TRUE(cpfio.IsReadOnly("@products@/testfile.xml"));
         EXPECT_TRUE(!cpfio.IsReadOnly("@log@/unittesttemp/realfileforunittest.xml"));
 
 
@@ -714,10 +714,10 @@ namespace UnitTest
 
         // find files test.
         AZ::IO::FixedMaxPath resolvedTestFilePath;
-        EXPECT_TRUE(cpfio.ResolvePath(resolvedTestFilePath, AZ::IO::PathView("@assets@/testfile.xml")));
+        EXPECT_TRUE(cpfio.ResolvePath(resolvedTestFilePath, AZ::IO::PathView("@products@/testfile.xml")));
         bool foundIt = false;
         // note that this file exists only in the archive.
-        cpfio.FindFiles("@assets@", "*.xml", [&foundIt, &cpfio, &resolvedTestFilePath](const char* foundName)
+        cpfio.FindFiles("@products@", "*.xml", [&foundIt, &cpfio, &resolvedTestFilePath](const char* foundName)
             {
                 AZ::IO::FixedMaxPath resolvedFoundPath;
                 EXPECT_TRUE(cpfio.ResolvePath(resolvedFoundPath, AZ::IO::PathView(foundName)));
@@ -734,10 +734,10 @@ namespace UnitTest
 
 
         // The following test is disabled because it will trigger an AZ_ERROR which will affect the outcome of this entire test
-        // EXPECT_NE(ResultCode::Success, cpfio.Remove("@assets@/testfile.xml")); // may not delete archive files
+        // EXPECT_NE(ResultCode::Success, cpfio.Remove("@products@/testfile.xml")); // may not delete archive files
 
         // make sure it works with and without alias:
-        EXPECT_TRUE(cpfio.Exists("@assets@/testfile.xml"));
+        EXPECT_TRUE(cpfio.Exists("@products@/testfile.xml"));
         EXPECT_TRUE(cpfio.Exists("testfile.xml"));
 
         EXPECT_TRUE(cpfio.Exists("@log@/unittesttemp/realfileforunittest.xml"));
@@ -788,22 +788,22 @@ namespace UnitTest
         EXPECT_TRUE(archive->ClosePack(realNameBuf));
 
         // change its actual location:
-        EXPECT_TRUE(archive->OpenPack("@assets@", realNameBuf));
-        EXPECT_TRUE(archive->IsFileExist("@assets@/foundit.dat"));
+        EXPECT_TRUE(archive->OpenPack("@products@", realNameBuf));
+        EXPECT_TRUE(archive->IsFileExist("@products@/foundit.dat"));
         EXPECT_FALSE(archive->IsFileExist("@usercache@/foundit.dat")); // do not find it in the previous location!
-        EXPECT_FALSE(archive->IsFileExist("@assets@/foundit.dat", AZ::IO::IArchive::eFileLocation_OnDisk));
-        EXPECT_FALSE(archive->IsFileExist("@assets@/notfoundit.dat"));
+        EXPECT_FALSE(archive->IsFileExist("@products@/foundit.dat", AZ::IO::IArchive::eFileLocation_OnDisk));
+        EXPECT_FALSE(archive->IsFileExist("@products@/notfoundit.dat"));
         EXPECT_TRUE(archive->ClosePack(realNameBuf));
 
         // try sub-folders
-        EXPECT_TRUE(archive->OpenPack("@assets@/mystuff", realNameBuf));
-        EXPECT_TRUE(archive->IsFileExist("@assets@/mystuff/foundit.dat"));
-        EXPECT_FALSE(archive->IsFileExist("@assets@/foundit.dat")); // do not find it in the previous locations!
+        EXPECT_TRUE(archive->OpenPack("@products@/mystuff", realNameBuf));
+        EXPECT_TRUE(archive->IsFileExist("@products@/mystuff/foundit.dat"));
+        EXPECT_FALSE(archive->IsFileExist("@products@/foundit.dat")); // do not find it in the previous locations!
         EXPECT_FALSE(archive->IsFileExist("@usercache@/foundit.dat")); // do not find it in the previous locations!
-        EXPECT_FALSE(archive->IsFileExist("@assets@/foundit.dat", AZ::IO::IArchive::eFileLocation_OnDisk));
-        EXPECT_FALSE(archive->IsFileExist("@assets@/mystuff/foundit.dat", AZ::IO::IArchive::eFileLocation_OnDisk));
-        EXPECT_FALSE(archive->IsFileExist("@assets@/notfoundit.dat")); // non-existent file
-        EXPECT_FALSE(archive->IsFileExist("@assets@/mystuff/notfoundit.dat")); // non-existent file
+        EXPECT_FALSE(archive->IsFileExist("@products@/foundit.dat", AZ::IO::IArchive::eFileLocation_OnDisk));
+        EXPECT_FALSE(archive->IsFileExist("@products@/mystuff/foundit.dat", AZ::IO::IArchive::eFileLocation_OnDisk));
+        EXPECT_FALSE(archive->IsFileExist("@products@/notfoundit.dat")); // non-existent file
+        EXPECT_FALSE(archive->IsFileExist("@products@/mystuff/notfoundit.dat")); // non-existent file
         EXPECT_TRUE(archive->ClosePack(realNameBuf));
     }
 
@@ -861,7 +861,7 @@ namespace UnitTest
         AZ::IO::FileIOBase* ioBase = AZ::IO::FileIOBase::GetInstance();
         ASSERT_NE(nullptr, ioBase);
 
-        const char* assetsPath = ioBase->GetAlias("@assets@");
+        const char* assetsPath = ioBase->GetAlias("@products@");
         ASSERT_NE(nullptr, assetsPath);
 
         auto stringToAdd = AZ::IO::Path(assetsPath) / "textures" / "test.dds";
@@ -872,7 +872,7 @@ namespace UnitTest
         // it normalizes the string, so the slashes flip and everything is lowercased.
         AZ::IO::FixedMaxPath resolvedAddedPath;
         AZ::IO::FixedMaxPath resolvedResourcePath;
-        EXPECT_TRUE(ioBase->ReplaceAlias(resolvedAddedPath, "@assets@/textures/test.dds"));
+        EXPECT_TRUE(ioBase->ReplaceAlias(resolvedAddedPath, "@products@/textures/test.dds"));
         EXPECT_TRUE(ioBase->ReplaceAlias(resolvedResourcePath, reslist->GetFirst()));
         EXPECT_EQ(resolvedAddedPath, resolvedResourcePath);
         reslist->Clear();

+ 45 - 0
Code/Framework/AzFramework/Tests/FileIO.cpp

@@ -802,6 +802,51 @@ namespace UnitTest
             AZ_TEST_STOP_TRACE_SUPPRESSION(1);
         }
 
+        TEST_F(AliasTest, GetAlias_LogsError_WhenAccessingDeprecatedAlias_Succeeds)
+        {
+            AZ::IO::LocalFileIO local;
+
+            AZ::IO::FixedMaxPathString aliasFolder;
+            EXPECT_TRUE(local.ConvertToAbsolutePath("/temp", aliasFolder.data(), aliasFolder.capacity()));
+            aliasFolder.resize_no_construct(AZStd::char_traits<char>::length(aliasFolder.data()));
+
+            local.SetAlias("@test@", aliasFolder.c_str());
+            local.SetDeprecatedAlias("@deprecated@", "@test@");
+            local.SetDeprecatedAlias("@deprecatednonexistent@", "@nonexistent@");
+            local.SetDeprecatedAlias("@deprecatedsecond@", "@deprecated@");
+            local.SetDeprecatedAlias("@deprecatednonaliaspath@", aliasFolder);
+
+            AZ_TEST_START_TRACE_SUPPRESSION;
+            const char* testAlias = local.GetAlias("@test@");
+            ASSERT_NE(nullptr, testAlias);
+            EXPECT_EQ(AZ::IO::PathView(aliasFolder), AZ::IO::PathView(testAlias));
+            AZ_TEST_STOP_TRACE_SUPPRESSION(0);
+
+            // Validate that accessing Deprecated Alias results in AZ_Error
+            AZ_TEST_START_TRACE_SUPPRESSION;
+            testAlias = local.GetAlias("@deprecated@");
+            ASSERT_NE(nullptr, testAlias);
+            EXPECT_EQ(AZ::IO::PathView(aliasFolder), AZ::IO::PathView(testAlias));
+            AZ_TEST_STOP_TRACE_SUPPRESSION(1);
+
+            AZ_TEST_START_TRACE_SUPPRESSION;
+            testAlias = local.GetAlias("@deprecatednonexistent@");
+            EXPECT_EQ(nullptr, testAlias);
+            AZ_TEST_STOP_TRACE_SUPPRESSION(1);
+
+            AZ_TEST_START_TRACE_SUPPRESSION;
+            testAlias = local.GetAlias("@deprecatedsecond@");
+            ASSERT_NE(nullptr, testAlias);
+            EXPECT_EQ(AZ::IO::PathView(aliasFolder), AZ::IO::PathView(testAlias));
+            AZ_TEST_STOP_TRACE_SUPPRESSION(1);
+
+            AZ_TEST_START_TRACE_SUPPRESSION;
+            testAlias = local.GetAlias("@deprecatednonaliaspath@");
+            ASSERT_NE(nullptr, testAlias);
+            EXPECT_EQ(AZ::IO::PathView(aliasFolder), AZ::IO::PathView(testAlias));
+            AZ_TEST_STOP_TRACE_SUPPRESSION(1);
+        }
+
         class SmartMoveTests
             : public FolderFixture
         {

+ 9 - 9
Code/Framework/AzFramework/Tests/FileTagTests.cpp

@@ -27,7 +27,7 @@ namespace UnitTest
 
     const char DummyFile[] = "dummy.txt";
     const char AnotherDummyFile[] = "Foo/Dummy.txt";
-    
+
     const char DummyPattern[] = R"(^(.+)_([a-z]+)\..+$)";
     const char MatchingPatternFile[] = "Foo/dummy_abc.txt";
     const char NonMatchingPatternFile[] = "Foo/dummy_a8c.txt";
@@ -75,7 +75,7 @@ namespace UnitTest
         : public AllocatorsFixture
     {
     public:
-        
+
         void SetUp() override
         {
             AllocatorsFixture::SetUp();
@@ -89,7 +89,7 @@ namespace UnitTest
             const char* testAssetRoot = m_tempDirectory.GetDirectory();
 
             // Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is
-            // shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash 
+            // shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash
             // in the unit tests.
             AZ::UserSettingsComponentRequestBus::Broadcast(&AZ::UserSettingsComponentRequests::DisableSaveOnFinalize);
 
@@ -98,7 +98,7 @@ namespace UnitTest
             AZ::IO::FileIOBase::SetInstance(nullptr);
             AZ::IO::FileIOBase::SetInstance(m_data->m_localFileIO.get());
 
-            AZ::IO::FileIOBase::GetInstance()->SetAlias("@assets@", testAssetRoot);
+            AZ::IO::FileIOBase::GetInstance()->SetAlias("@products@", testAssetRoot);
 
             m_data->m_excludeFileQueryManager = AZStd::make_unique<FileTagQueryManagerTest>(FileTagType::Exclude);
             m_data->m_includeFileQueryManager = AZStd::make_unique<FileTagQueryManagerTest>(FileTagType::Include);
@@ -114,7 +114,7 @@ namespace UnitTest
 
             AZStd::vector<AZStd::string> includedWildcardTags = { DummyFileTags[DummyFileTagIndex::GIdx] };
             EXPECT_TRUE(m_data->m_fileTagManager.AddFilePatternTags(DummyWildcard, FilePatternType::Wildcard, FileTagType::Include, includedWildcardTags).IsSuccess());
-            
+
             AzFramework::StringFunc::Path::Join(testAssetRoot, AZStd::string::format("%s.%s", ExcludeFile, FileTagAsset::Extension()).c_str(), m_data->m_excludeFile);
 
             AzFramework::StringFunc::Path::Join(testAssetRoot, AZStd::string::format("%s.%s", IncludeFile, FileTagAsset::Extension()).c_str(), m_data->m_includeFile);
@@ -184,7 +184,7 @@ namespace UnitTest
     TEST_F(FileTagTest, FileTags_QueryByAbsoluteFilePath_Valid)
     {
         AZStd::string absoluteDummyFilePath = DummyFile;
-        EXPECT_TRUE(AzFramework::StringFunc::AssetDatabasePath::Join("@assets@", absoluteDummyFilePath.c_str(), absoluteDummyFilePath));
+        EXPECT_TRUE(AzFramework::StringFunc::AssetDatabasePath::Join("@products@", absoluteDummyFilePath.c_str(), absoluteDummyFilePath));
 
         AZStd::set<AZStd::string> tags = m_data->m_excludeFileQueryManager->GetTags(absoluteDummyFilePath);
 
@@ -196,7 +196,7 @@ namespace UnitTest
         ASSERT_EQ(tags.size(), 0);
 
         AZStd::string absoluteAnotherDummyFilePath = AnotherDummyFile;
-        EXPECT_TRUE(AzFramework::StringFunc::AssetDatabasePath::Join("@assets@", absoluteAnotherDummyFilePath.c_str(), absoluteAnotherDummyFilePath));
+        EXPECT_TRUE(AzFramework::StringFunc::AssetDatabasePath::Join("@products@", absoluteAnotherDummyFilePath.c_str(), absoluteAnotherDummyFilePath));
 
         tags = m_data->m_includeFileQueryManager->GetTags(absoluteAnotherDummyFilePath);
         ASSERT_EQ(tags.size(), 2);
@@ -213,7 +213,7 @@ namespace UnitTest
 
         // Set the customized alias
         AZStd::string customizedAliasFilePath;
-        const char* assetsAlias = AZ::IO::FileIOBase::GetInstance()->GetAlias("@assets@");
+        const char* assetsAlias = AZ::IO::FileIOBase::GetInstance()->GetAlias("@products@");
         AzFramework::StringFunc::AssetDatabasePath::Join(assetsAlias, "foo", customizedAliasFilePath);
         AZ::IO::FileIOBase::GetInstance()->SetAlias("@customizedalias@", customizedAliasFilePath.c_str());
 
@@ -305,7 +305,7 @@ namespace UnitTest
 
         m_data->m_excludeFileQueryManager->ClearData();
         EXPECT_TRUE(m_data->m_excludeFileQueryManager->Load(m_data->m_excludeFile));
-        
+
         AZStd::set<AZStd::string> outputTags = m_data->m_excludeFileQueryManager->GetTags(MatchingWildcardFile);
 
         EXPECT_EQ(outputTags.size(), 2);

+ 41 - 69
Code/Framework/AzFramework/Tests/GenAppDescriptors.cpp

@@ -6,81 +6,19 @@
  *
  */
 
+#include <AzCore/IO/FileIO.h>
 #include <AzCore/Serialization/SerializeContext.h>
 #include <AzCore/Serialization/ObjectStream.h>
 #include <AzCore/UnitTest/TestTypes.h>
-#include <AzFramework/IO/LocalFileIO.h>
-#include <AzCore/Component/ComponentApplication.h>
+#include <AzTest/Utils.h>
+#include <AzFramework/Application/Application.h>
 
 namespace UnitTest
 {
-    using namespace AZ;
-
-    class FileIOBaseRAII
-    {
-    public:
-        FileIOBaseRAII(AZ::IO::FileIOBase& fileIO)
-            : m_prevFileIO(AZ::IO::FileIOBase::GetInstance())
-        {
-            AZ::IO::FileIOBase::SetInstance(&fileIO);
-        }
-
-        ~FileIOBaseRAII()
-        {
-            AZ::IO::FileIOBase::SetInstance(m_prevFileIO);
-        }
-    private:
-        AZ::IO::FileIOBase* m_prevFileIO;
-    };
-
     class GenAppDescriptors
         : public AllocatorsTestFixture
     {
     public:
-
-        void run()
-        {
-            struct Config
-            {
-                const char* platformName;
-                const char* configName;
-                const char* libSuffix;
-            };
-
-            ComponentApplication app;
-
-            SerializeContext serializeContext;
-            AZ::ComponentApplication::Descriptor::Reflect(&serializeContext, &app);
-            AZ::Entity::Reflect(&serializeContext);
-            DynamicModuleDescriptor::Reflect(&serializeContext);
-
-            AZ::Entity dummySystemEntity(AZ::SystemEntityId, "SystemEntity");
-
-            const Config config = {"Platform", "Config", "libSuffix"};
-
-            AZ::ComponentApplication::Descriptor descriptor;
-
-            if (config.libSuffix && config.libSuffix[0])
-            {
-                FakePopulateModules(descriptor, config.libSuffix);
-            }
-
-            const AZStd::string filename = AZStd::string::format("LYConfig_%s%s.xml", config.platformName, config.configName);
-
-            IO::FileIOStream stream(filename.c_str(), IO::OpenMode::ModeWrite);
-            ObjectStream* objStream = ObjectStream::Create(&stream, serializeContext, ObjectStream::ST_XML);
-            bool descWriteOk = objStream->WriteClass(&descriptor);
-            (void)descWriteOk;
-            AZ_Warning("ComponentApplication", descWriteOk, "Failed to write memory descriptor to application descriptor file %s!", filename.c_str());
-            bool entityWriteOk = objStream->WriteClass(&dummySystemEntity);
-            (void)entityWriteOk;
-            AZ_Warning("ComponentApplication", entityWriteOk, "Failed to write system entity to application descriptor file %s!", filename.c_str());
-            bool flushOk = objStream->Finalize();
-            (void)flushOk;
-            AZ_Warning("ComponentApplication", flushOk, "Failed finalizing application descriptor file %s!", filename.c_str());
-
-        }
-
         void FakePopulateModules(AZ::ComponentApplication::Descriptor& desc, const char* libSuffix)
         {
             static const char* modules[] =
@@ -100,10 +38,44 @@ namespace UnitTest
         }
     };
 
-    TEST_F(GenAppDescriptors, Test)
+    TEST_F(GenAppDescriptors, WriteDescriptor_ToXML_Succeeds)
     {
-        AZ::IO::LocalFileIO fileIO;
-        FileIOBaseRAII restoreFileIOScope(fileIO);
-        run();
+        struct Config
+        {
+            const char* platformName;
+            const char* configName;
+            const char* libSuffix;
+        };
+
+        AzFramework::Application app;
+
+        AZ::SerializeContext serializeContext;
+        AZ::ComponentApplication::Descriptor::Reflect(&serializeContext, &app);
+        AZ::Entity::Reflect(&serializeContext);
+        AZ::DynamicModuleDescriptor::Reflect(&serializeContext);
+
+        AZ::Entity dummySystemEntity(AZ::SystemEntityId, "SystemEntity");
+
+        const Config config = {"Platform", "Config", "libSuffix"};
+
+        AZ::ComponentApplication::Descriptor descriptor;
+
+        if (config.libSuffix && config.libSuffix[0])
+        {
+            FakePopulateModules(descriptor, config.libSuffix);
+        }
+
+        AZ::Test::ScopedAutoTempDirectory tempDirectory;
+        const auto filename = AZ::IO::Path(tempDirectory.GetDirectory()) /
+            AZStd::string::format("LYConfig_%s%s.xml", config.platformName, config.configName);
+
+        AZ::IO::FileIOStream stream(filename.c_str(), AZ::IO::OpenMode::ModeWrite);
+        auto objStream = AZ::ObjectStream::Create(&stream, serializeContext, AZ::ObjectStream::ST_XML);
+        const bool descWriteOk = objStream->WriteClass(&descriptor);
+        EXPECT_TRUE(descWriteOk) << "Failed to write memory descriptor to application descriptor file " << filename.c_str() << "!";
+        const bool entityWriteOk = objStream->WriteClass(&dummySystemEntity);
+        EXPECT_TRUE(entityWriteOk) << "Failed to write system entity to application descriptor file " << filename.c_str() << "!";
+        const bool flushOk = objStream->Finalize();
+        EXPECT_TRUE(flushOk) << "Failed finalizing application descriptor file " << filename.c_str() << "!";
     }
 }

+ 2 - 2
Code/Framework/AzGameFramework/AzGameFramework/Application/GameApplication.cpp

@@ -38,12 +38,12 @@ namespace AzGameFramework
         {
             // fall back to checking Project Cache Root.
             enginePakPath /= "engine.pak";
-            enginePakOpened = m_archive->OpenPack("@projectproductassets@", enginePakPath.Native());
+            enginePakOpened = m_archive->OpenPack("@products@", enginePakPath.Native());
         }
         if (!enginePakOpened)
         {
             enginePakPath = AZ::IO::FixedMaxPath(AZ::Utils::GetExecutableDirectory()) / "engine.pak";
-            m_archive->OpenPack("@projectproductassets@", enginePakPath.Native());
+            m_archive->OpenPack("@products@", enginePakPath.Native());
         }
     }
 

+ 1 - 1
Code/Framework/AzNetworking/AzNetworking/Utilities/EncryptionCommon.cpp

@@ -107,7 +107,7 @@ namespace AzNetworking
         if (AZ::IO::FileIOBase::GetInstance() != nullptr)
         {
             char buffer[AZ_MAX_PATH_LEN];
-            AZ::IO::FileIOBase::GetInstance()->ResolvePath("@assets@/", buffer, sizeof(buffer));
+            AZ::IO::FileIOBase::GetInstance()->ResolvePath("@products@/", buffer, sizeof(buffer));
             assetDir = AZStd::string(buffer);
         }
 

+ 0 - 8
Code/Framework/AzToolsFramework/AzToolsFramework/API/EditorAssetSystemAPI.h

@@ -47,14 +47,6 @@ namespace AzToolsFramework
 
             //! Retrieve the absolute path for the Asset Database Location
             virtual bool GetAbsoluteAssetDatabaseLocation(AZStd::string& /*result*/) { return false; }
-
-            //! Retrieve the absolute folder path to the current game's source assets (the ones that go into source control)
-            //! This may include the current mod path, if a mod is being edited by the editor
-            virtual const char* GetAbsoluteDevGameFolderPath() = 0;
-
-            //! Retrieve the absolute folder path to the current developer root ('dev'), which contains source artifacts
-            //! and is generally checked into source control.
-            virtual const char* GetAbsoluteDevRootFolderPath() = 0;
         
             /// Convert a full source path like "c:\\dev\\gamename\\blah\\test.tga" into a relative product path.
             /// asset paths never mention their alias and are relative to the asset cache root

+ 0 - 20
Code/Framework/AzToolsFramework/AzToolsFramework/Asset/AssetSystemComponent.cpp

@@ -354,26 +354,6 @@ namespace AzToolsFramework
             }
         }
 
-        const char* AssetSystemComponent::GetAbsoluteDevGameFolderPath()
-        {
-            AZ::IO::FileIOBase* fileIO = AZ::IO::FileIOBase::GetInstance();
-            if (fileIO)
-            {
-                return fileIO->GetAlias("@devassets@");
-            }
-            return "";
-        }
-
-        const char* AssetSystemComponent::GetAbsoluteDevRootFolderPath()
-        {
-            AZ::IO::FileIOBase* fileIO = AZ::IO::FileIOBase::GetInstance();
-            if (fileIO)
-            {
-                return fileIO->GetAlias("@devroot@");
-            }
-            return "";
-        }
-
         void AssetSystemComponent::OnSystemTick()
         {
             AssetSystemBus::ExecuteQueuedEvents();

+ 0 - 2
Code/Framework/AzToolsFramework/AzToolsFramework/Asset/AssetSystemComponent.h

@@ -56,8 +56,6 @@ namespace AzToolsFramework
             //////////////////////////////////////////////////////////////////////////
             // AzToolsFramework::AssetSystemRequestBus::Handler overrides
             bool GetAbsoluteAssetDatabaseLocation(AZStd::string& result) override;
-            const char* GetAbsoluteDevGameFolderPath() override;
-            const char* GetAbsoluteDevRootFolderPath() override;
             bool GetRelativeProductPathFromFullSourceOrProductPath(const AZStd::string& fullPath, AZStd::string& outputPath) override;
             bool GenerateRelativeSourcePath(
                 const AZStd::string& sourcePath, AZStd::string& outputPath, AZStd::string& watchFolder) override;

+ 1 - 1
Code/Framework/AzToolsFramework/AzToolsFramework/AssetEditor/AssetEditorWidget.cpp

@@ -137,7 +137,7 @@ namespace AzToolsFramework
         AssetEditorWidgetUserSettings::AssetEditorWidgetUserSettings()
         {
             char assetRoot[AZ_MAX_PATH_LEN] = { 0 };
-            AZ::IO::FileIOBase::GetInstance()->ResolvePath("@devassets@", assetRoot, AZ_MAX_PATH_LEN);
+            AZ::IO::FileIOBase::GetInstance()->ResolvePath("@projectroot@", assetRoot, AZ_MAX_PATH_LEN);
 
             m_lastSavePath = assetRoot;
         }

+ 1 - 1
Code/Framework/AzToolsFramework/AzToolsFramework/Debug/TraceContext.h

@@ -39,7 +39,7 @@ namespace AzToolsFramework
         //      the TraceContextLogFormatter.
         //
         //      Usage example:
-        //          const char* gameFolder = m_context.pRC->GetSystemEnvironment()->pFileIO->GetAlias("@devassets@");
+        //          const char* gameFolder = m_context.pRC->GetSystemEnvironment()->pFileIO->GetAlias("@projectroot@");
         //          AZ_TraceContext("Game folder", gameFolder);
         //
         //          for (int i=0; i<subMeshCount; ++i)

+ 1 - 1
Code/Framework/AzToolsFramework/AzToolsFramework/Logger/TraceLogger.cpp

@@ -66,7 +66,7 @@ namespace AzToolsFramework
         StringFunc::Path::Join(resolveBuffer, "log", logDirectory);
         fileIO->SetAlias("@log@", logDirectory.c_str());
 
-        fileIO->CreatePath("@root@");
+        fileIO->CreatePath("@products@");
         fileIO->CreatePath("@user@");
         fileIO->CreatePath("@log@");
 

+ 3 - 6
Code/Framework/AzToolsFramework/AzToolsFramework/Slice/SliceRequestComponent.cpp

@@ -8,6 +8,7 @@
 
 
 #include <AzCore/RTTI/BehaviorContext.h>
+#include <AzCore/Utils/Utils.h>
 #include <AzFramework/IO/FileOperations.h>
 #include <AzFramework/StringFunc/StringFunc.h>
 #include <AzToolsFramework/API/EditorAssetSystemAPI.h>
@@ -78,13 +79,9 @@ namespace AzToolsFramework
         AzToolsFramework::ToolsApplicationRequestBus::BroadcastResult(entitiesAndDescendants,
             &AzToolsFramework::ToolsApplicationRequestBus::Events::GatherEntitiesAndAllDescendents, AzToolsFramework::EntityIdList{ entityId });
 
-        // Retrieve the game folder so we can use that as a root with the passed in relative path
-        const char* gameFolder = nullptr;
-        AzToolsFramework::AssetSystemRequestBus::BroadcastResult(gameFolder, &AzToolsFramework::AssetSystem::AssetSystemRequest::GetAbsoluteDevGameFolderPath);
-
         // Join our relative path with the game folder to get a full path to the desired asset
-        AZStd::string assetFullPath;
-        AzFramework::StringFunc::Path::Join(gameFolder, assetPath, assetFullPath);
+        AZ::IO::FixedMaxPath assetFullPath = AZ::Utils::GetProjectPath();
+        assetFullPath /= assetPath;
 
         // Call SliceUtilities::MakeNewSlice with all user input prompts disabled
         bool success = AzToolsFramework::SliceUtilities::MakeNewSlice(entitiesAndDescendants,

+ 3 - 3
Code/Framework/AzToolsFramework/AzToolsFramework/Slice/SliceTransaction.cpp

@@ -718,7 +718,7 @@ namespace AzToolsFramework
 
             if (!fullPathFound)
             {
-                assetFullPath = AZStd::string::format("@devassets@/%s", sliceAssetPath.c_str());
+                assetFullPath = AZStd::string::format("@projectroot@/%s", sliceAssetPath.c_str());
             }
 
             return Commit(assetFullPath.c_str(), preSaveCallback, postSaveCallback, sliceCommitFlags);
@@ -1020,13 +1020,13 @@ namespace AzToolsFramework
                 AZ::IO::FileIOBase* fileIO = AZ::IO::FileIOBase::GetInstance();
                 AZ_Assert(fileIO, "File IO is not initialized.");
 
-                AZStd::string devAssetPath = fileIO->GetAlias("@devassets@");
+                AZStd::string devAssetPath = fileIO->GetAlias("@projectroot@");
                 AZStd::string userPath = fileIO->GetAlias("@user@");
                 AZStd::string tempPath = fullPath;
                 EBUS_EVENT(AzFramework::ApplicationRequests::Bus, NormalizePath, devAssetPath);
                 EBUS_EVENT(AzFramework::ApplicationRequests::Bus, NormalizePath, userPath);
                 EBUS_EVENT(AzFramework::ApplicationRequests::Bus, NormalizePath, tempPath);
-                AzFramework::StringFunc::Replace(tempPath, "@devassets@", devAssetPath.c_str());
+                AzFramework::StringFunc::Replace(tempPath, "@projectroot@", devAssetPath.c_str());
                 AzFramework::StringFunc::Replace(tempPath, devAssetPath.c_str(), userPath.c_str());
                 tempPath.append(".slicetemp");
 

+ 2 - 2
Code/Framework/AzToolsFramework/AzToolsFramework/ToolsComponents/EditorLayerComponent.cpp

@@ -378,11 +378,11 @@ namespace AzToolsFramework
             // If this layer is being loaded, it won't have a level save dependency yet, so clear that flag.
             m_mustSaveLevelWhenLayerSaves = false;
             QString fullPathName = levelPakFile;
-            if (fullPathName.contains("@devassets@"))
+            if (fullPathName.contains("@projectroot@"))
             {
                 AZ::IO::FileIOBase* fileIO = AZ::IO::FileIOBase::GetInstance();
                 // Resolving the path through resolvepath would normalize and lowcase it, and in this case, we don't want that.
-                fullPathName.replace("@devassets@", fileIO->GetAlias("@devassets@"));
+                fullPathName.replace("@projectroot@", fileIO->GetAlias("@projectroot@"));
             }
 
             QFileInfo fileNameInfo(fullPathName);

+ 1 - 1
Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp

@@ -315,7 +315,7 @@ namespace AzToolsFramework
             // temporarily null after QFileDialogs close, which we need in order to
             // be able to parent our message dialogs properly
             QWidget* activeWindow = QApplication::activeWindow();
-            const AZStd::string prefabFilesPath = "@devassets@/Prefabs";
+            const AZStd::string prefabFilesPath = "@projectroot@/Prefabs";
 
             // Remove Level entity if it's part of the list
             

+ 2 - 2
Code/Framework/AzToolsFramework/Tests/ArchiveTests.cpp

@@ -131,13 +131,13 @@ namespace UnitTest
                 m_app.reset(aznew ToolsTestApplication("ArchiveComponentTest"));
                 m_app->Start(AzFramework::Application::Descriptor());
                 // Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is
-                // shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash 
+                // shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash
                 // in the unit tests.
                 AZ::UserSettingsComponentRequestBus::Broadcast(&AZ::UserSettingsComponentRequests::DisableSaveOnFinalize);
 
                 if (auto fileIoBase = AZ::IO::FileIOBase::GetInstance(); fileIoBase != nullptr)
                 {
-                    fileIoBase->SetAlias("@assets@", m_tempDir.GetDirectory());
+                    fileIoBase->SetAlias("@products@", m_tempDir.GetDirectory());
                 }
             }
 

+ 15 - 19
Code/Framework/AzToolsFramework/Tests/AssetSeedManager.cpp

@@ -37,10 +37,10 @@ namespace // anonymous
 
     bool Search(const AzToolsFramework::AssetFileInfoList& assetList, const AZ::Data::AssetId& assetId)
     {
-        return AZStd::find_if(assetList.m_fileInfoList.begin(), assetList.m_fileInfoList.end(), 
-            [&](AzToolsFramework::AssetFileInfo fileInfo) 
-            { 
-                return fileInfo.m_assetId == assetId; 
+        return AZStd::find_if(assetList.m_fileInfoList.begin(), assetList.m_fileInfoList.end(),
+            [&](AzToolsFramework::AssetFileInfo fileInfo)
+            {
+                return fileInfo.m_assetId == assetId;
             });
     }
 }
@@ -74,11 +74,11 @@ namespace UnitTest
 
             m_application->Start(AzFramework::Application::Descriptor());
 
-            // By default @assets@ is setup to include the platform at the end. But this test is going to
+            // By default @products@ is setup to include the platform at the end. But this test is going to
             // loop over platforms and it will be included as part of the relative path of the file.
             // So the asset folder for these tests have to point to the cache project root folder, which
             // doesn't include the platform.
-            AZ::IO::FileIOBase::GetInstance()->SetAlias("@assets@", cacheProjectRootFolder.c_str());
+            AZ::IO::FileIOBase::GetInstance()->SetAlias("@products@", cacheProjectRootFolder.c_str());
 
             for (int idx = 0; idx < s_totalAssets; idx++)
             {
@@ -158,17 +158,17 @@ namespace UnitTest
             m_assetRegistry->RegisterAssetDependency(assets[5], AZ::Data::ProductDependency(assets[6], 0));
             m_assetRegistry->RegisterAssetDependency(assets[6], AZ::Data::ProductDependency(assets[7], 0));
 
-            // asset8 -> asset6 
+            // asset8 -> asset6
             m_assetRegistry->RegisterAssetDependency(assets[8], AZ::Data::ProductDependency(assets[6], 0));
 
-            // asset10 -> asset11 
+            // asset10 -> asset11
             m_assetRegistry->RegisterAssetDependency(assets[10], AZ::Data::ProductDependency(assets[11], 0));
 
-            // asset11 -> asset10 
+            // asset11 -> asset10
             m_assetRegistry->RegisterAssetDependency(assets[11], AZ::Data::ProductDependency(assets[10], 0));
 
             // Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is
-            // shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash 
+            // shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash
             // in the unit tests.
             AZ::UserSettingsComponentRequestBus::Broadcast(&AZ::UserSettingsComponentRequests::DisableSaveOnFinalize);
 
@@ -203,10 +203,6 @@ namespace UnitTest
 
             const AZStd::string engroot = AZ::Test::GetEngineRootPath();
             AZ::IO::FileIOBase::GetInstance()->SetAlias("@engroot@", engroot.c_str());
-
-            AZ::IO::Path assetRoot(AZ::Utils::GetProjectPath());
-            assetRoot /= "Cache";
-            AZ::IO::FileIOBase::GetInstance()->SetAlias("@root@", assetRoot.c_str());
         }
 
         void TearDown() override
@@ -219,7 +215,7 @@ namespace UnitTest
             delete m_application;
         }
 
-        AZ::Data::AssetInfo GetAssetInfoById(const AZ::Data::AssetId& id) override 
+        AZ::Data::AssetInfo GetAssetInfoById(const AZ::Data::AssetId& id) override
         {
             auto foundIter = m_assetRegistry->m_assetIdToInfo.find(id);
             if (foundIter != m_assetRegistry->m_assetIdToInfo.end())
@@ -540,7 +536,7 @@ namespace UnitTest
             EXPECT_TRUE(Search(assetList, assets[7]));
             EXPECT_TRUE(Search(assetList, assets[8]));
 
-            // Removing the android flag from the asset should still produce the same result 
+            // Removing the android flag from the asset should still produce the same result
             m_assetSeedManager->RemoveSeedAsset(assets[8], AzFramework::PlatformFlags::Platform_ANDROID);
 
             assetList = m_assetSeedManager->GetDependencyList(AzFramework::PlatformId::PC);
@@ -564,7 +560,7 @@ namespace UnitTest
             EXPECT_TRUE(Search(assetList, assets[3]));
             EXPECT_TRUE(Search(assetList, assets[4]));
 
-            // Adding the android flag again to the asset 
+            // Adding the android flag again to the asset
             m_assetSeedManager->AddSeedAsset(assets[8], AzFramework::PlatformFlags::Platform_ANDROID);
             assetList = m_assetSeedManager->GetDependencyList(AzFramework::PlatformId::ANDROID_ID);
 
@@ -624,7 +620,7 @@ namespace UnitTest
 
             EXPECT_EQ(assetList1.m_fileInfoList[0].m_assetId, assetList2.m_fileInfoList[0].m_assetId);
             EXPECT_GE(assetList2.m_fileInfoList[0].m_modificationTime, assetList1.m_fileInfoList[0].m_modificationTime); // file mod time should change
-            
+
             // file hash should not change
             for (int idx = 0; idx < 5; idx++)
             {
@@ -680,7 +676,7 @@ namespace UnitTest
             m_assetSeedManager->AddSeedAsset(assets[validFileIndex], AzFramework::PlatformFlags::Platform_PC, m_assetsPath[invalidFileIndex]);
 
             const AzFramework::AssetSeedList& oldSeedList = m_assetSeedManager->GetAssetSeedList();
-            
+
             for (const auto& seedInfo : oldSeedList)
             {
                 if (seedInfo.m_assetId == assets[validFileIndex])

+ 0 - 2
Code/Framework/AzToolsFramework/Tests/AssetSystemMocks.h

@@ -18,8 +18,6 @@ namespace UnitTests
     {
     public:
         MOCK_METHOD1(GetAbsoluteAssetDatabaseLocation, bool(AZStd::string&));
-        MOCK_METHOD0(GetAbsoluteDevGameFolderPath, const char* ());
-        MOCK_METHOD0(GetAbsoluteDevRootFolderPath, const char* ());
         MOCK_METHOD2(GetRelativeProductPathFromFullSourceOrProductPath, bool(const AZStd::string& fullPath, AZStd::string& relativeProductPath));
         MOCK_METHOD3(GenerateRelativeSourcePath,
             bool(const AZStd::string& sourcePath, AZStd::string& relativePath, AZStd::string& watchFolder));

+ 2 - 2
Code/Framework/AzToolsFramework/Tests/EntityTestbed.h

@@ -181,8 +181,8 @@ namespace UnitTest
 
             const char* dir = m_componentApplication->GetExecutableFolder();
 
-            m_localFileIO.SetAlias("@assets@", dir);
-            m_localFileIO.SetAlias("@devassets@", dir);
+            m_localFileIO.SetAlias("@products@", dir);
+            m_localFileIO.SetAlias("@projectroot@", dir);
         }
 
         void Destroy()

+ 4 - 4
Code/Framework/AzToolsFramework/Tests/PlatformAddressedAssetCatalogTests.cpp

@@ -22,7 +22,7 @@
 #include <AzCore/UserSettings/UserSettingsComponent.h>
 #include <Utils/Utils.h>
 
-namespace 
+namespace
 {
     static const int s_totalAssets = 12;
 }
@@ -53,15 +53,15 @@ namespace UnitTest
 
             m_application->Start(AzFramework::Application::Descriptor());
             // Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is
-            // shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash 
+            // shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash
             // in the unit tests.
             AZ::UserSettingsComponentRequestBus::Broadcast(&AZ::UserSettingsComponentRequests::DisableSaveOnFinalize);
 
-            // By default @assets@ is setup to include the platform at the end. But this test is going to
+            // By default @products@ is setup to include the platform at the end. But this test is going to
             // loop over all platforms and it will be included as part of the relative path of the file.
             // So the asset folder for these tests have to point to the cache project root folder, which
             // doesn't include the platform.
-            AZ::IO::FileIOBase::GetInstance()->SetAlias("@assets@", cacheProjectRootFolder.c_str());
+            AZ::IO::FileIOBase::GetInstance()->SetAlias("@products@", cacheProjectRootFolder.c_str());
 
             for (int platformNum = AzFramework::PlatformId::PC; platformNum < AzFramework::PlatformId::NumPlatformIds; ++platformNum)
             {

+ 0 - 2
Code/Framework/AzToolsFramework/Tests/SliceStabilityTests/SliceStabilityTestFramework.h

@@ -142,8 +142,6 @@ namespace UnitTest
         /*
         * AssetSystemRequestBus
         */
-        const char* GetAbsoluteDevGameFolderPath() override { return ""; }
-        const char* GetAbsoluteDevRootFolderPath() override { return ""; }
         bool GetRelativeProductPathFromFullSourceOrProductPath([[maybe_unused]] const AZStd::string& fullPath, [[maybe_unused]] AZStd::string& relativeProductPath) override { return false; }
         bool GenerateRelativeSourcePath(
             [[maybe_unused]] const AZStd::string& sourcePath, [[maybe_unused]] AZStd::string& relativePath,

+ 3 - 3
Code/Legacy/CryCommon/CryPath.h

@@ -520,14 +520,14 @@ namespace PathUtil
         unsigned int index = 0;
         if (relativePath.length() && relativePath[index] == '@') // already aliased
         {
-            if (AZ::StringFunc::Equal(relativePath.c_str(), "@assets@/", false, 9))
+            if (AZ::StringFunc::Equal(relativePath.c_str(), "@products@/", false, 9))
             {
                 return relativePath.substr(9); // assets is assumed.
             }
             return relativePath;
         }
 
-        const char* rootValue = gEnv->pFileIO->GetAlias("@root@");
+        const char* rootValue = gEnv->pFileIO->GetAlias("@products@");
         if (rootValue)
         {
             stack_string rootPath(ToUnixPath(rootValue));
@@ -538,7 +538,7 @@ namespace PathUtil
                 )
             {
                 stack_string chopped_string = relativePath.substr(rootPath.size());
-                stack_string rooted = stack_string("@root@") + chopped_string;
+                stack_string rooted = stack_string("@products@") + chopped_string;
                 return rooted;
             }
         }

+ 3 - 3
Code/Legacy/CrySystem/ConsoleBatchFile.cpp

@@ -59,10 +59,10 @@ bool CConsoleBatchFile::ExecuteConfigFile(const char* sFilename)
 
     AZStd::string filename;
 
-    if (sFilename[0] != '@') // console config files are actually by default in @root@ instead of @assets@
+    if (sFilename[0] != '@') // console config files are actually by default in @products@ instead of @products@
     {
         // However, if we've passed in a relative or absolute path that matches an existing file name,
-        // don't change it.  Only change it to "@root@/filename" and strip off any relative paths
+        // don't change it.  Only change it to "@products@/filename" and strip off any relative paths
         // if the given pattern *didn't* match a file.
         if (AZ::IO::FileIOBase::GetDirectInstance()->Exists(sFilename))
         {
@@ -70,7 +70,7 @@ bool CConsoleBatchFile::ExecuteConfigFile(const char* sFilename)
         }
         else
         {
-            filename = PathUtil::Make("@root@", PathUtil::GetFile(sFilename));
+            filename = PathUtil::Make("@products@", PathUtil::GetFile(sFilename));
         }
     }
     else

+ 1 - 1
Code/Legacy/CrySystem/DebugCallStack.cpp

@@ -372,7 +372,7 @@ void DebugCallStack::LogExceptionInfo(EXCEPTION_POINTERS* pex)
         const char* logAlias = gEnv->pFileIO->GetAlias("@log@");
         if (!logAlias)
         {
-            logAlias = gEnv->pFileIO->GetAlias("@root@");
+            logAlias = gEnv->pFileIO->GetAlias("@products@");
         }
         if (logAlias)
         {

+ 1 - 1
Code/Legacy/CrySystem/LevelSystem/LevelSystem.cpp

@@ -306,7 +306,7 @@ void CLevelSystem::ScanFolder(const char* subfolder, bool modFolder)
                 }
 
                 AZStd::string levelContainerPakPath;
-                AZ::StringFunc::Path::Join("@assets@", m_levelsFolder.c_str(), levelContainerPakPath);
+                AZ::StringFunc::Path::Join("@products@", m_levelsFolder.c_str(), levelContainerPakPath);
                 if (subfolder && subfolder[0])
                 {
                     AZ::StringFunc::Path::Join(levelContainerPakPath.c_str(), subfolder, levelContainerPakPath);

+ 5 - 5
Code/Legacy/CrySystem/SystemCFG.cpp

@@ -292,10 +292,10 @@ static bool ParseSystemConfig(const AZStd::string& strSysConfigFilePath, ILoadCo
             // to either root or assets/config.  this is done so that code can just request a simple file name and get its data
             if (
                 !(file.Open(filename.c_str(), "rb")) &&
-                !(file.Open((AZStd::string("@root@/") + filename).c_str(), "rb")) &&
-                !(file.Open((AZStd::string("@assets@/") + filename).c_str(), "rb")) &&
-                !(file.Open((AZStd::string("@assets@/config/") + filename).c_str(), "rb")) &&
-                !(file.Open((AZStd::string("@assets@/config/spec/") + filename).c_str(), "rb"))
+                !(file.Open((AZStd::string("@products@/") + filename).c_str(), "rb")) &&
+                !(file.Open((AZStd::string("@products@/") + filename).c_str(), "rb")) &&
+                !(file.Open((AZStd::string("@products@/config/") + filename).c_str(), "rb")) &&
+                !(file.Open((AZStd::string("@products@/config/spec/") + filename).c_str(), "rb"))
                 )
             {
                 if (warnIfMissing)
@@ -414,7 +414,7 @@ static bool ParseSystemConfig(const AZStd::string& strSysConfigFilePath, ILoadCo
                     // replace '\\\\' with '\\' and '\\\"' with '\"'
                     AZ::StringFunc::Replace(strValue, "\\\\", "\\");
                     AZ::StringFunc::Replace(strValue, "\\\"", "\"");
-                    
+
                     pSink->OnLoadConfigurationEntry(strKey.c_str(), strValue.c_str(), strGroup.c_str());
                 }
             }

+ 3 - 6
Code/Legacy/CrySystem/SystemInit.cpp

@@ -796,7 +796,7 @@ void CSystem::OpenBasicPaks()
     bBasicPaksLoaded = true;
 
     // open pak files
-    constexpr AZStd::string_view paksFolder = "@assets@/*.pak"; // (@assets@ assumed)
+    constexpr AZStd::string_view paksFolder = "@products@/*.pak"; // (@products@ assumed)
     m_env.pCryPak->OpenPacks(paksFolder);
 
     InlineInitializationProcessing("CSystem::OpenBasicPaks OpenPacks( paksFolder.c_str() )");
@@ -805,7 +805,7 @@ void CSystem::OpenBasicPaks()
     // Open engine packs
     //////////////////////////////////////////////////////////////////////////
 
-    const char* const assetsDir = "@assets@";
+    const char* const assetsDir = "@products@";
 
     // After game paks to have same search order as with files on disk
     m_env.pCryPak->OpenPack(assetsDir, "engine.pak");
@@ -874,7 +874,7 @@ void CSystem::OpenLanguageAudioPak([[maybe_unused]] const char* sLanguage)
 
     if (!AZ::StringFunc::Equal(sLocalizationFolder, "Languages", false))
     {
-        sLocalizationFolder = "@assets@";
+        sLocalizationFolder = "@products@";
     }
 
     // load localized pak with crc32 filenames on consoles to save memory.
@@ -1260,9 +1260,6 @@ AZ_POP_DISABLE_WARNING
 
         InlineInitializationProcessing("CSystem::Init Create console");
 
-        // Need to load the engine.pak that includes the config files needed during initialization
-        m_env.pCryPak->OpenPack("@assets@", "engine.pak");
-
         InitFileSystem_LoadEngineFolders(startupParams);
 
 #if !defined(RELEASE) || defined(RELEASE_LOGGING)

+ 1 - 1
Code/Tools/AWSNativeSDKInit/source/Platform/Android/InitializeCerts_Android.cpp

@@ -27,7 +27,7 @@ namespace AWSNativeSDKInit
         void CopyCaCertBundle()
         {
             AZStd::vector<char> contents;
-            AZStd::string certificatePath = "@assets@/certificates/aws/cacert.pem";
+            AZStd::string certificatePath = "@products@/certificates/aws/cacert.pem";
             AZStd::string publicStoragePath = AZ::Android::Utils::GetAppPublicStoragePath();
             publicStoragePath.append("/certificates/aws/cacert.pem");
 

+ 6 - 11
Code/Tools/AssetProcessor/AssetBuilder/AssetBuilderComponent.cpp

@@ -683,31 +683,26 @@ void AssetBuilderComponent::ProcessJob(const AssetBuilderSDK::ProcessJobFunction
     AZ_Assert(settingsRegistry != nullptr, "SettingsRegistry must be ready for use in the AssetBuilder.");
 
     // The root path is the cache plus the platform name.
-    AZ::IO::FixedMaxPath newRoot(m_gameCache);
+    AZ::IO::FixedMaxPath newProjectCache(m_gameCache);
     // Check if the platform identifier is a valid "asset platform"
     // If so, use it, other wise use the OS default platform as a fail safe
     // This is to make sure the "debug platform" isn't added as a path segment
-    // the Cache Root folder
+    // the Cache ProjectCache folder
     if (AzFramework::PlatformHelper::GetPlatformIdFromName(request.m_platformInfo.m_identifier) != AzFramework::PlatformId::Invalid)
     {
-        newRoot /= request.m_platformInfo.m_identifier;
+        newProjectCache /= request.m_platformInfo.m_identifier;
     }
     else
     {
-        newRoot /= AzFramework::OSPlatformToDefaultAssetPlatform(AZ_TRAIT_OS_PLATFORM_CODENAME);
+        newProjectCache /= AzFramework::OSPlatformToDefaultAssetPlatform(AZ_TRAIT_OS_PLATFORM_CODENAME);
     }
 
-    // The asset path is root and the lower case game name.
-    AZ::IO::FixedMaxPath newAssets = newRoot;
-
     // Now set the paths and run the job.
     {
         // Save out the prior paths.
-        ScopedAliasSetter assetAliasScope(*ioBase, "@assets@", newAssets.c_str());
-        ScopedAliasSetter rootAliasScope(*ioBase, "@root@", newRoot.c_str());
-        ScopedAliasSetter projectplatformCacheAliasScope(*ioBase, "@projectplatformcache@", newRoot.c_str());
+        ScopedAliasSetter projectPlatformCacheAliasScope(*ioBase, "@products@", newProjectCache.c_str());
         ScopedSettingsRegistrySetter cacheRootFolderScope(*settingsRegistry,
-            AZ::SettingsRegistryMergeUtils::FilePathKey_CacheRootFolder, newRoot.Native());
+            AZ::SettingsRegistryMergeUtils::FilePathKey_CacheRootFolder, newProjectCache.Native());
 
         // Invoke the Process Job function
         job(request, outResponse);

+ 14 - 32
Code/Tools/AssetProcessor/native/AssetManager/AssetCatalog.cpp

@@ -27,7 +27,7 @@ namespace AssetProcessor
         , m_registryBuiltOnce(false)
         , m_registriesMutex(QMutex::Recursive)
     {
-        
+
         for (const AssetBuilderSDK::PlatformInfo& info : m_platformConfig->GetEnabledPlatforms())
         {
             m_platforms.push_back(QString::fromUtf8(info.m_identifier.c_str()));
@@ -38,17 +38,9 @@ namespace AssetProcessor
 
         // save 30mb for this.  Really large projects do get this big (and bigger)
         // if you don't do this, things get fragmented very fast.
-        m_saveBuffer.reserve(1024 * 1024 * 30); 
-        
-        m_absoluteDevFolderPath[0] = 0;
-        m_absoluteDevGameFolderPath[0] = 0;
-
-        AZStd::string engineRoot;
-        AzFramework::ApplicationRequests::Bus::BroadcastResult(engineRoot, &AzFramework::ApplicationRequests::GetEngineRoot);
-        azstrcpy(m_absoluteDevFolderPath, AZ_MAX_PATH_LEN, engineRoot.c_str());
+        m_saveBuffer.reserve(1024 * 1024 * 30);
 
-        AZStd::string gameFolderPath{AssetUtilities::ComputeProjectPath().toUtf8().constData()};
-        azstrcpy(m_absoluteDevGameFolderPath, AZ_MAX_PATH_LEN, gameFolderPath.c_str());
+        AssetUtilities::ComputeProjectPath();
 
         AssetUtilities::ComputeProjectCacheRoot(m_cacheRootDir);
 
@@ -359,7 +351,7 @@ namespace AssetProcessor
                             [[maybe_unused]] bool makeDirResult = AZ::IO::SystemFile::CreateDir(absPath.toUtf8().constData());
                             AZ_Warning(AssetProcessor::ConsoleChannel, makeDirResult, "Failed create folder %s", platformCacheDir.toUtf8().constData());
                         }
-                        
+
                         // if we succeeded in doing this, then use "rename" to move the file over the previous copy.
                         bool moved = AssetUtilities::MoveFileWithTimeout(tempRegistryFile, actualRegistryFile, 3);
                         allCatalogsSaved = allCatalogsSaved && moved;
@@ -382,7 +374,7 @@ namespace AssetProcessor
                 }
             }
         }
-        
+
         {
             // scoped to minimize the duration of this mutex lock
             QMutexLocker locker(&m_savingRegistryMutex);
@@ -605,7 +597,7 @@ namespace AssetProcessor
 
         AZStd::string nameForMap(relativeFilePath.toUtf8().constData());
         AZStd::to_lower(nameForMap.begin(), nameForMap.end());
-       
+
         m_sourceNameToSourceUUIDMap.insert({ nameForMap, sourceUuid });
     }
 
@@ -627,16 +619,6 @@ namespace AssetProcessor
 
     //////////////////////////////////////////////////////////////////////////
 
-    const char* AssetCatalog::GetAbsoluteDevGameFolderPath()
-    {
-        return m_absoluteDevGameFolderPath;
-    }
-
-    const char* AssetCatalog::GetAbsoluteDevRootFolderPath()
-    {
-        return m_absoluteDevFolderPath;
-    }
-
     bool AssetCatalog::GetRelativeProductPathFromFullSourceOrProductPath(const AZStd::string& fullSourceOrProductPath, AZStd::string& relativeProductPath)
     {
         ProcessGetRelativeProductPathFromFullSourceOrProductPathRequest(fullSourceOrProductPath, relativeProductPath);
@@ -976,7 +958,7 @@ namespace AssetProcessor
 
         // regardless of which way we come into this function we must always use ConvertToRelativePath
         // to convert from whatever the input format is to a database path (which may include output prefix)
-        QString databaseName; 
+        QString databaseName;
         QString scanFolder;
         if (!AzFramework::StringFunc::Path::IsRelative(sourcePath))
         {
@@ -1163,7 +1145,7 @@ namespace AssetProcessor
     AZStd::string AssetCatalog::GetAssetPathById(const AZ::Data::AssetId& id)
     {
         return GetAssetInfoById(id).m_relativePath;
-        
+
     }
 
     AZ::Data::AssetId AssetCatalog::GetAssetIdByPath(const char* path, const AZ::Data::AssetType& typeToRegister, bool autoRegisterIfNotFound)
@@ -1175,7 +1157,7 @@ namespace AssetProcessor
         AZStd::string relProductPath;
         GetRelativeProductPathFromFullSourceOrProductPath(path, relProductPath);
         QString tempPlatformName = GetDefaultAssetPlatform();
-        
+
         AZ::Data::AssetId assetId;
         {
             QMutexLocker locker(&m_registriesMutex);
@@ -1344,7 +1326,7 @@ namespace AssetProcessor
             //remove aliases if present
             normalisedAssetPath = AssetUtilities::NormalizeAndRemoveAlias(normalisedAssetPath);
 
-            if (!normalisedAssetPath.isEmpty()) // this happens if it comes in as just for example "@assets@/"
+            if (!normalisedAssetPath.isEmpty()) // this happens if it comes in as just for example "@products@/"
             {
                 AZStd::lock_guard<AZStd::mutex> lock(m_databaseMutex);
 
@@ -1439,7 +1421,7 @@ namespace AssetProcessor
                     relativePath = entry.m_sourceName;
 
                     watchFolder = scanEntry.m_scanFolder;
-                    
+
 
                     return true;
                 }
@@ -1489,7 +1471,7 @@ namespace AssetProcessor
         {
             return foundIter->second;
         }
-         
+
         // we did not find it - try the backup mapping!
         AssetId legacyMapping = registryToUse.GetAssetIdByLegacyAssetId(assetId);
         if (legacyMapping.IsValid())
@@ -1533,7 +1515,7 @@ namespace AssetProcessor
 
             return !assetInfo.m_relativePath.empty();
         }
-        
+
         // Asset isn't in the DB or in the APM queue, we don't know what this asset ID is
         return false;
     }
@@ -1588,7 +1570,7 @@ namespace AssetProcessor
                     AZStd::string sourceFileFullPath;
                     AzFramework::StringFunc::Path::Join(watchFolder.c_str(), assetInfo.m_relativePath.c_str(), sourceFileFullPath);
                     assetInfo.m_sizeBytes = AZ::IO::SystemFile::Length(sourceFileFullPath.c_str());
-                    
+
                     assetInfo.m_assetType = AZ::Uuid::CreateNull(); // most source files don't have a type!
 
                     // Go through the list of source assets and see if this asset's file path matches any of the filters

+ 0 - 4
Code/Tools/AssetProcessor/native/AssetManager/AssetCatalog.h

@@ -88,8 +88,6 @@ namespace AssetProcessor
 
         //////////////////////////////////////////////////////////////////////////
         // AzToolsFramework::AssetSystem::AssetSystemRequestBus::Handler overrides
-        const char* GetAbsoluteDevGameFolderPath() override;
-        const char* GetAbsoluteDevRootFolderPath() override;
         bool GetRelativeProductPathFromFullSourceOrProductPath(const AZStd::string& fullPath, AZStd::string& relativeProductPath) override;
 
         //! Given a partial or full source file path, respond with its relative path and the watch folder it is relative to.
@@ -215,8 +213,6 @@ namespace AssetProcessor
 
         AZStd::vector<char> m_saveBuffer; // so that we don't realloc all the time
 
-        char m_absoluteDevFolderPath[AZ_MAX_PATH_LEN];
-        char m_absoluteDevGameFolderPath[AZ_MAX_PATH_LEN];
         QDir m_cacheRootDir;
     };
 }

Beberapa file tidak ditampilkan karena terlalu banyak file yang berubah dalam diff ini