void BuiltinResources::preprocess(bool forceImport, time_t lastUpdateTime)
	{
		// Hidden dependency: Textures need to be generated before shaders as they may use the default textures
		generateTextures();

		Path dataListsFilePath = mBuiltinRawDataFolder + DATA_LIST_JSON;
		SPtr<DataStream> dataListStream = FileSystem::openFile(dataListsFilePath);
		json dataListJSON = json::parse(dataListStream->getAsString().c_str());

		json skinJSON = dataListJSON["Skin"];
		json cursorsJSON = dataListJSON["Cursors"];
		json iconsJSON = dataListJSON["Icons"];
		json includesJSON = dataListJSON["Includes"];
		json shadersJSON = dataListJSON["Shaders"];

		Path rawSkinFolder = mBuiltinRawDataFolder + SKIN_FOLDER;
		Path rawCursorFolder = mBuiltinRawDataFolder + CURSOR_FOLDER;
		Path rawIconFolder = mBuiltinRawDataFolder + ICON_FOLDER;
		Path rawShaderFolder = mBuiltinRawDataFolder + SHADER_FOLDER;
		Path rawShaderIncludeFolder = mBuiltinRawDataFolder + SHADER_INCLUDE_FOLDER;

		// Update DataList.json if needed
		bool updatedDataLists = false;
		updatedDataLists |= BuiltinResourcesHelper::updateJSON(
			rawCursorFolder,
			BuiltinResourcesHelper::AssetType::Normal,
			cursorsJSON);

		updatedDataLists |= BuiltinResourcesHelper::updateJSON(
			rawIconFolder,
			BuiltinResourcesHelper::AssetType::Normal,
			iconsJSON);

		updatedDataLists |= BuiltinResourcesHelper::updateJSON(
			rawShaderIncludeFolder,
			BuiltinResourcesHelper::AssetType::Normal,
			includesJSON);

		updatedDataLists |= BuiltinResourcesHelper::updateJSON(
			rawShaderFolder,
			BuiltinResourcesHelper::AssetType::Normal,
			shadersJSON);

		updatedDataLists |= BuiltinResourcesHelper::updateJSON(
			rawSkinFolder,
			BuiltinResourcesHelper::AssetType::Sprite,
			skinJSON);

		dataListStream->close();

		if(updatedDataLists)
		{
			FileSystem::remove(dataListsFilePath);

			dataListJSON["Skin"] = skinJSON;
			dataListJSON["Cursors"] = cursorsJSON;
			dataListJSON["Icons"] = iconsJSON;
			dataListJSON["Includes"] = includesJSON;
			dataListJSON["Shaders"] = shadersJSON;

			String jsonString = dataListJSON.dump(4).c_str();
			dataListStream = FileSystem::createAndOpenFile(dataListsFilePath);
			dataListStream->writeString(jsonString);
			dataListStream->close();
		}

		Path skinFolder = mBuiltinDataFolder + SKIN_FOLDER;
		Path iconFolder = mBuiltinDataFolder + ICON_FOLDER;
		Path shaderIncludeFolder = mBuiltinDataFolder + SHADER_INCLUDE_FOLDER;
		Path shaderDependenciesFile = mBuiltinDataFolder + "ShaderDependencies.json";

		// If forcing import, clear all data folders since everything will be recreated anyway
		if(forceImport)
		{
			FileSystem::remove(mEngineCursorFolder);
			FileSystem::remove(iconFolder);
			FileSystem::remove(shaderIncludeFolder);
			FileSystem::remove(mEngineShaderFolder);
			FileSystem::remove(skinFolder);
			
			FileSystem::remove(shaderDependenciesFile);
		}

		// Read shader dependencies JSON
		json shaderDependenciesJSON;
		if(FileSystem::exists(shaderDependenciesFile))
		{
			SPtr<DataStream> stream = FileSystem::openFile(shaderDependenciesFile);
			shaderDependenciesJSON = json::parse(stream->getAsString().c_str());
			stream->close();
		}

		// Import cursors
		{
			BuiltinResourcesHelper::updateManifest(
				mEngineCursorFolder,
				cursorsJSON,
				mResourceManifest,
				BuiltinResourcesHelper::AssetType::Normal);

			Vector<bool> importFlags = BuiltinResourcesHelper::generateImportFlags(
				cursorsJSON,
				rawCursorFolder,
				lastUpdateTime,
				forceImport);

			BuiltinResourcesHelper::importAssets(
				cursorsJSON,
				importFlags,
				rawCursorFolder,
				mEngineCursorFolder,
				mResourceManifest,
				BuiltinResourcesHelper::AssetType::Normal);
		}

		// Import icons
		{
			BuiltinResourcesHelper::updateManifest(
				iconFolder,
				iconsJSON,
				mResourceManifest,
				BuiltinResourcesHelper::AssetType::Normal);

			Vector<bool> importFlags = BuiltinResourcesHelper::generateImportFlags(
				iconsJSON,
				rawIconFolder,
				lastUpdateTime,
				forceImport);

			BuiltinResourcesHelper::importAssets(
				iconsJSON,
				importFlags,
				rawIconFolder,
				iconFolder,
				mResourceManifest,
				BuiltinResourcesHelper::AssetType::Normal);
		}

		// Import shaders
		{
			BuiltinResourcesHelper::updateManifest(
				shaderIncludeFolder,
				includesJSON,
				mResourceManifest,
				BuiltinResourcesHelper::AssetType::Normal);

			BuiltinResourcesHelper::updateManifest(
				mEngineShaderFolder,
				shadersJSON,
				mResourceManifest,
				BuiltinResourcesHelper::AssetType::Normal);

			Vector<bool> includeImportFlags = BuiltinResourcesHelper::generateImportFlags(
				includesJSON,
				rawShaderIncludeFolder,
				lastUpdateTime,
				forceImport);

			Vector<bool> shaderImportFlags = BuiltinResourcesHelper::generateImportFlags(
				shadersJSON,
				rawShaderFolder,
				lastUpdateTime,
				forceImport,
				&shaderDependenciesJSON,
				rawShaderIncludeFolder);

			// Hidden dependency: Includes must be imported before shaders, but import flags for shaders must be generated
			// before includes are imported, since the process checks if imports changed
			BuiltinResourcesHelper::importAssets(
				includesJSON,
				includeImportFlags,
				rawShaderIncludeFolder,
				shaderIncludeFolder,
				mResourceManifest,
				BuiltinResourcesHelper::AssetType::Normal,
				nullptr,
				true);


			BuiltinResourcesHelper::importAssets(
				shadersJSON,
				shaderImportFlags,
				rawShaderFolder,
				mEngineShaderFolder,
				mResourceManifest,
				BuiltinResourcesHelper::AssetType::Normal,
				&shaderDependenciesJSON,
				true);
		}

		// Import GUI sprites
		{
			BuiltinResourcesHelper::updateManifest(
				skinFolder,
				skinJSON,
				mResourceManifest,
				BuiltinResourcesHelper::AssetType::Sprite);

			Vector<bool> skinImportFlags = BuiltinResourcesHelper::generateImportFlags(
				skinJSON,
				rawSkinFolder,
				lastUpdateTime,
				forceImport);

			BuiltinResourcesHelper::importAssets(
				skinJSON,
				skinImportFlags,
				rawSkinFolder,
				skinFolder,
				mResourceManifest,
				BuiltinResourcesHelper::AssetType::Sprite);
		}

		// Update shader dependencies JSON
		{
			String jsonString = shaderDependenciesJSON.dump(4).c_str();

			dataListStream = FileSystem::createAndOpenFile(shaderDependenciesFile);
			dataListStream->writeString(jsonString);
			dataListStream->close();
		}

		// Import font
		BuiltinResourcesHelper::importFont(
			mBuiltinRawDataFolder + DefaultFontFilename,
			DefaultFontFilename,
			mBuiltinDataFolder,
			{ DefaultFontSize },
			false,
			UUID("c9f08cab-f9c9-47c4-96e0-1066a8d4455b"),
			mResourceManifest);

		// Generate & save GUI skin
		{
			SPtr<GUISkin> skin = generateGUISkin();
			Path outputPath = mBuiltinDataFolder + (GUISkinFile + u8".asset");

			HResource skinResource = gResources()._createResourceHandle(skin, UUID("c1bf9a9d-4355-4841-a538-25e67730ec4b"));

			gResources().save(skinResource, outputPath, true);
			mResourceManifest->registerResource(skinResource.getUUID(), outputPath);
		}

		// Generate & save meshes
		generateMeshes();
		
		Resources::instance().unloadAllUnused();
	}
	void BuiltinEditorResources::preprocess(bool forceImport, time_t lastUpdateTime)
	{
		Resources::instance().unloadAllUnused();

		Path dataListsFilePath = BuiltinRawDataFolder + DATA_LIST_JSON;
		SPtr<DataStream> dataListStream = FileSystem::openFile(dataListsFilePath);

		json dataListJSON = json::parse(dataListStream->getAsString().c_str());

		json skinJSON = dataListJSON["Skin"];
		json iconsJSON = dataListJSON["Icons"];
		json includesJSON = dataListJSON["Includes"];
		json shadersJSON = dataListJSON["Shaders"];

		// Update DataList.json if needed
		bool updatedDataLists = false;
		updatedDataLists |= BuiltinResourcesHelper::updateJSON(
			EditorRawIconsFolder,
			BuiltinResourcesHelper::AssetType::Sprite,
			iconsJSON);

		updatedDataLists |= BuiltinResourcesHelper::updateJSON(
			EditorRawShaderIncludeFolder,
			BuiltinResourcesHelper::AssetType::Normal,
			includesJSON);

		updatedDataLists |= BuiltinResourcesHelper::updateJSON(
			EditorRawShaderFolder,
			BuiltinResourcesHelper::AssetType::Normal,
			shadersJSON);

		updatedDataLists |= BuiltinResourcesHelper::updateJSON(
			EditorRawSkinFolder,
			BuiltinResourcesHelper::AssetType::Sprite,
			skinJSON);

		dataListStream->close();

		if (updatedDataLists)
		{
			FileSystem::remove(dataListsFilePath);

			dataListJSON["Skin"] = skinJSON;
			dataListJSON["Icons"] = iconsJSON;
			dataListJSON["Includes"] = includesJSON;
			dataListJSON["Shaders"] = shadersJSON;

			String jsonString = dataListJSON.dump(4).c_str();
			dataListStream = FileSystem::createAndOpenFile(dataListsFilePath);
			dataListStream->writeString(jsonString);
			dataListStream->close();
		}

		// If forcing import, clear all data folders since everything will be recreated anyway
		Path shaderDependenciesFile = BuiltinDataFolder + "ShaderDependencies.json";
		if(forceImport)
		{
			FileSystem::remove(EditorIconFolder);
			FileSystem::remove(EditorShaderIncludeFolder);
			FileSystem::remove(EditorShaderFolder);
			FileSystem::remove(EditorSkinFolder);
			
			FileSystem::remove(shaderDependenciesFile);
		}

		// Read shader dependencies JSON
		json shaderDependenciesJSON;
		if(FileSystem::exists(shaderDependenciesFile))
		{
			SPtr<DataStream> stream = FileSystem::openFile(shaderDependenciesFile);
			shaderDependenciesJSON = json::parse(stream->getAsString().c_str());
			stream->close();
		}

		// Import icons
		{
			BuiltinResourcesHelper::updateManifest(
				EditorIconFolder,
				iconsJSON,
				mResourceManifest,
				BuiltinResourcesHelper::AssetType::Sprite);

			Vector<bool> importFlags = BuiltinResourcesHelper::generateImportFlags(
				iconsJSON,
				EditorRawIconsFolder,
				lastUpdateTime,
				forceImport);

			BuiltinResourcesHelper::importAssets(
				iconsJSON,
				importFlags,
				EditorRawIconsFolder,
				EditorIconFolder,
				mResourceManifest,
				BuiltinResourcesHelper::AssetType::Sprite);
		}

		// Import shaders
		{
			BuiltinResourcesHelper::updateManifest(
				EditorShaderIncludeFolder,
				includesJSON,
				mResourceManifest,
				BuiltinResourcesHelper::AssetType::Normal);

			BuiltinResourcesHelper::updateManifest(
				EditorShaderFolder,
				shadersJSON,
				mResourceManifest,
				BuiltinResourcesHelper::AssetType::Normal);

			Vector<bool> includeImportFlags = BuiltinResourcesHelper::generateImportFlags(
				includesJSON,
				EditorRawShaderIncludeFolder,
				lastUpdateTime,
				forceImport);

			Vector<bool> shaderImportFlags = BuiltinResourcesHelper::generateImportFlags(
				shadersJSON,
				EditorRawShaderFolder,
				lastUpdateTime,
				forceImport,
				&shaderDependenciesJSON,
				EditorRawShaderIncludeFolder);

			// Hidden dependency: Includes must be imported before shaders, but import flags for shaders must be generated
			// before includes are imported, since the process checks if imports changed
			BuiltinResourcesHelper::importAssets(
				includesJSON,
				includeImportFlags,
				EditorRawShaderIncludeFolder,
				EditorShaderIncludeFolder,
				mResourceManifest,
				BuiltinResourcesHelper::AssetType::Normal);

			BuiltinResourcesHelper::importAssets(
				shadersJSON,
				shaderImportFlags,
				EditorRawShaderFolder,
				EditorShaderFolder,
				mResourceManifest,
				BuiltinResourcesHelper::AssetType::Normal,
				&shaderDependenciesJSON);
		}

		// Import GUI sprites
		{
			BuiltinResourcesHelper::updateManifest(
				EditorSkinFolder,
				skinJSON,
				mResourceManifest,
				BuiltinResourcesHelper::AssetType::Sprite);

			Vector<bool> includeImportFlags = BuiltinResourcesHelper::generateImportFlags(
				skinJSON,
				EditorRawSkinFolder,
				lastUpdateTime,
				forceImport);

			BuiltinResourcesHelper::importAssets(
				skinJSON,
				includeImportFlags,
				EditorRawSkinFolder,
				EditorSkinFolder,
				mResourceManifest,
				BuiltinResourcesHelper::AssetType::Sprite);
		}

		// Update shader dependencies JSON
		{
			String jsonString = shaderDependenciesJSON.dump(4).c_str();

			dataListStream = FileSystem::createAndOpenFile(shaderDependenciesFile);
			dataListStream->writeString(jsonString);
			dataListStream->close();
		}

		// Import fonts
		BuiltinResourcesHelper::importFont(
			BuiltinRawDataFolder + DefaultFontFilename,
			DefaultFontFilename,
			BuiltinDataFolder,
			{ DefaultFontSize },
			true,
			UUID("6ce69053-00d7-4c60-a229-249b8d8fd60e"),
			mResourceManifest);

		BuiltinResourcesHelper::importFont(
			BuiltinRawDataFolder + DefaultFontFilename,
			DefaultAAFontFilename,
			BuiltinDataFolder,
			{ TitleFontSize },
			true,
			UUID("10999b74-d976-4116-9f72-21e489a7a8e4"),
			mResourceManifest);

		// Import splash screen
		{
			Path inputPath = BuiltinRawDataFolder + String(SplashScreenName);
			Path outputPath = BuiltinDataFolder + (String(SplashScreenName) + ".asset");

			auto textureIO = gImporter().createImportOptions<TextureImportOptions>(inputPath);
			textureIO->setCPUCached(true);
			textureIO->setGenerateMipmaps(false);
			HTexture splashTexture = gImporter().import<Texture>(inputPath, textureIO);

			SPtr<PixelData> splashPixelData = splashTexture->getProperties().allocBuffer(0, 0);
			splashTexture->readCachedData(*splashPixelData);

			FileEncoder fe(outputPath);
			fe.encode(splashPixelData.get());
		}

		// Generate & save GUI skin
		{
			SPtr<GUISkin> skin = generateGUISkin();
			Path outputPath = BuiltinDataFolder + (GUISkinFile + ".asset");

			HResource skinResource = gResources()._createResourceHandle(skin, UUID("ec0ea68d-efa5-4a3b-a6fc-b15aaec9689f"));

			gResources().save(skinResource, outputPath, true);
			mResourceManifest->registerResource(skinResource.getUUID(), outputPath);
		}

		Resources::instance().unloadAllUnused();
	}