Space Ad Mobile 9.5.1
Space Ad Mobile makes ads easy to add into your mobile game: one unified workflow for LevelPlay, AdMob, Yandex.Ads across banners, interstitials and rewarded.
Loading...
Searching...
No Matches
YandexEditorDummyUpdater.cs
1using System;
2using System.IO;
3using System.Text;
4using System.Text.RegularExpressions;
5using UnityEngine;
6
7namespace SCG.SpaceAdMobile
8{
19 internal static class YandexEditorDummyUpdater
20 {
21 #region Constants
22
24 private const string AssetsFolder = "Assets";
25
27 private const string FactoryFileName = "YandexMobileAdsClientFactory.cs";
28
30 private const string GeneratedFolderName = "SCG.EditorDummies";
31
33 private const string FactoryPatchMarker = "SCG_ENHANCED_EDITOR_FACTORY_PATCH";
34
35 #endregion
36
37 #region Fields
38
40 private static readonly Encoding s_utf8Encoding = new UTF8Encoding(false);
41
43 private static readonly string[] s_generatedFileNames =
44 {
45 "SCGEditorRewardedAdDummy.cs",
46 "SCGEditorInterstitialAdDummy.cs",
47 "SCGEditorAppOpenAdDummy.cs",
48 "SCGEditorBannerClient.cs"
49 };
50
57 private static (string FileName, string Content)[] GetGeneratedFiles(YandexData yandexData) => new[]
58 {
59 (s_generatedFileNames[0], yandexData.RewardedDummyTemplate),
60 (s_generatedFileNames[1], yandexData.InterstitialDummyTemplate),
61 (s_generatedFileNames[2], yandexData.AppOpenDummyTemplate),
62 (s_generatedFileNames[3], yandexData.BannerDummyTemplate)
63 };
64
69 private static readonly (string MethodName, string StockReturnLine, string PatchedReturnLine)[] s_factoryPatches =
70 {
71 CreateFactoryPatch("BuildBannerClient", "DummyBannerClient", "SCGEditorBannerClient"),
72 CreateFactoryPatch("BuildInterstitialAdLoaderClient", "DummyInterstitialAdLoaderClient", "SCGEditorInterstitialAdLoaderClient"),
73 CreateFactoryPatch("BuildRewardedAdLoaderClient", "DummyRewardedAdLoaderClient", "SCGEditorRewardedAdLoaderClient"),
74 CreateFactoryPatch("BuildAppOpenAdLoaderClient", "DummyAppOpenAdLoaderClient", "SCGEditorAppOpenAdLoaderClient")
75 };
76
84 private static (string MethodName, string StockReturnLine, string PatchedReturnLine) CreateFactoryPatch(
85 string methodName, string stockTypeName, string enhancedTypeName) =>
86 (methodName, $"return new {stockTypeName}();", $"return new {enhancedTypeName}(); // {FactoryPatchMarker}");
87
88 #endregion
89
99 internal static bool ApplyOrRestore(YandexData yandexData, bool useEnhancedEditorDummies)
100 {
101 if (UnityEditor.AssetDatabase.IsAssetImportWorkerProcess())
102 return false;
103
104 if (yandexData == null)
105 throw new ArgumentNullException(nameof(yandexData));
106
107 if (useEnhancedEditorDummies)
108 {
109 var generatedChanged = EnsureGeneratedFiles(yandexData);
110 var factoryChanged = TryUpdateFactory(yandexData, true, out var appliedFactoryPatch);
111 return generatedChanged || (factoryChanged && appliedFactoryPatch);
112 }
113
114 if (!TryUpdateFactory(yandexData, false, out var restoredFactoryPatch))
115 return false;
116
117 var deletedFiles = DeleteGeneratedFiles(yandexData);
118 return restoredFactoryPatch || deletedFiles;
119 }
120
121 #region Generated files
122
128 private static bool EnsureGeneratedFiles(YandexData yandexData)
129 {
130 var folderPath = GetGeneratedFolderPath(yandexData);
131 var absoluteFolderPath = ToAbsolutePath(folderPath);
132 Directory.CreateDirectory(absoluteFolderPath);
133 var changed = false;
134
135 foreach (var (fileName, content) in GetGeneratedFiles(yandexData))
136 {
137 if (string.IsNullOrWhiteSpace(content))
138 {
139 Debug.LogWarning($"[{nameof(YandexEditorDummyUpdater)}]: {fileName} template is empty in {nameof(AdAggregatorResource)}.");
140 continue;
141 }
142
143 var assetsRelativePath = $"{folderPath}/{fileName}";
144 var absolutePath = ToAbsolutePath(assetsRelativePath);
145 var currentContent = File.Exists(absolutePath) ? File.ReadAllText(absolutePath) : null;
146
147 if (string.Equals(currentContent, content, StringComparison.Ordinal))
148 continue;
149
150 File.WriteAllText(absolutePath, content, s_utf8Encoding);
151 Debug.Log($"[{nameof(YandexEditorDummyUpdater)}]: Updated {fileName}.");
152 changed = true;
153 }
154
155 return changed;
156 }
157
163 private static bool DeleteGeneratedFiles(YandexData yandexData)
164 {
165 var folderPath = GetGeneratedFolderPath(yandexData);
166 var changed = false;
167
168 // ReSharper disable once LoopCanBeConvertedToQuery
169 foreach (var fileName in s_generatedFileNames)
170 {
171 changed |= DeleteFileWithMeta($"{folderPath}/{fileName}");
172 }
173
174 return DeleteDirectoryIfEmpty(folderPath) || changed;
175 }
176
181 private static bool DeleteFileWithMeta(string assetsRelativePath)
182 {
183 var absolutePath = ToAbsolutePath(assetsRelativePath);
184 var changed = false;
185
186 if (File.Exists(absolutePath))
187 {
188 File.Delete(absolutePath);
189 Debug.Log($"[{nameof(YandexEditorDummyUpdater)}]: Deleted {Path.GetFileName(assetsRelativePath)}.");
190 changed = true;
191 }
192
193 var metaPath = absolutePath + ".meta";
194 // ReSharper disable once InvertIf
195 if (File.Exists(metaPath))
196 {
197 File.Delete(metaPath);
198 changed = true;
199 }
200
201 return changed;
202 }
203
208 private static bool DeleteDirectoryIfEmpty(string assetsRelativePath)
209 {
210 var absolutePath = ToAbsolutePath(assetsRelativePath);
211 if (!Directory.Exists(absolutePath))
212 return false;
213
214 if (Directory.GetFiles(absolutePath).Length > 0 || Directory.GetDirectories(absolutePath).Length > 0)
215 return false;
216
217 Directory.Delete(absolutePath);
218
219 var metaPath = absolutePath + ".meta";
220 if (File.Exists(metaPath))
221 {
222 File.Delete(metaPath);
223 }
224
225 return true;
226 }
227
233 private static string GetGeneratedFolderPath(YandexData yandexData) =>
234 $"{ToAssetsPath(yandexData.AssetPath)}/Common/{GeneratedFolderName}";
235
236 #endregion
237
238 #region Factory patch
239
253 private static bool TryUpdateFactory(YandexData yandexData, bool applyEnhancedDummies, out bool changed)
254 {
255 var factoryPath = GetFactoryPath(yandexData);
256 var absolutePath = ToAbsolutePath(factoryPath);
257 changed = false;
258
259 if (!File.Exists(absolutePath))
260 {
261 Debug.LogWarning(
262 $"[{nameof(YandexEditorDummyUpdater)}]: {FactoryFileName} was not found at '{factoryPath}'.");
263 return false;
264 }
265
266 var original = File.ReadAllText(absolutePath);
267 var updated = original;
268
269 foreach (var factoryPatch in s_factoryPatches)
270 {
271 if (TryPatchFactoryMethod(updated, factoryPatch, applyEnhancedDummies, out updated)) continue;
272 Debug.LogWarning(
273 $"[{nameof(YandexEditorDummyUpdater)}]: Unable to {(applyEnhancedDummies ? "apply" : "restore")} editor patch for {factoryPatch.MethodName}.");
274 return false;
275 }
276
277 if (string.Equals(original, updated, StringComparison.Ordinal))
278 return true;
279
280 File.WriteAllText(absolutePath, updated, s_utf8Encoding);
281 changed = true;
282 Debug.Log(
283 $"[{nameof(YandexEditorDummyUpdater)}]: {(applyEnhancedDummies ? "Applied" : "Restored")} editor factory patch.");
284 return true;
285 }
286
296 private static bool TryPatchFactoryMethod(
297 string source,
298 (string MethodName, string StockReturnLine, string PatchedReturnLine) patch,
299 bool applyEnhancedDummies,
300 out string updated)
301 {
302 var sourceLine = applyEnhancedDummies ? patch.StockReturnLine : patch.PatchedReturnLine;
303 var targetLine = applyEnhancedDummies ? patch.PatchedReturnLine : patch.StockReturnLine;
304 var methodPattern =
305 $@"(internal static [^\r\n]+ {Regex.Escape(patch.MethodName)}\‍([^)]*\‍)\s*\{{\s*#if UNITY_EDITOR\s*){Regex.Escape(sourceLine)}(?=\s*#elif)";
306
307 var regex = new Regex(methodPattern, RegexOptions.Singleline | RegexOptions.CultureInvariant);
308 var match = regex.Match(source);
309 if (match.Success)
310 {
311 updated = regex.Replace(source, currentMatch => currentMatch.Groups[1].Value + targetLine, 1);
312 return true;
313 }
314
315 var currentPattern =
316 $@"internal static [^\r\n]+ {Regex.Escape(patch.MethodName)}\‍([^)]*\‍)\s*\{{\s*#if UNITY_EDITOR\s*{Regex.Escape(targetLine)}(?=\s*#elif)";
317 if (Regex.IsMatch(source, currentPattern, RegexOptions.Singleline | RegexOptions.CultureInvariant))
318 {
319 updated = source;
320 return true;
321 }
322
323 updated = source;
324 return false;
325 }
326
332 private static string GetFactoryPath(YandexData yandexData) =>
333 $"{ToAssetsPath(yandexData.AssetPath)}/Platforms/{FactoryFileName}";
334
335 #endregion
336
337 #region Path helpers
338
344 private static string ToAssetsPath(string possiblyRelativeToAssetsFolder)
345 {
346 var path = possiblyRelativeToAssetsFolder.Replace("\\", "/");
347 return path.StartsWith($"{AssetsFolder}/", StringComparison.Ordinal) ? path : $"{AssetsFolder}/{path}";
348 }
349
356 private static string ToAbsolutePath(string assetsRelativePath)
357 {
358 var normalizedPath = assetsRelativePath.Replace("\\", "/");
359 if (string.Equals(normalizedPath, AssetsFolder, StringComparison.Ordinal))
360 return Application.dataPath.Replace("\\", "/");
361
362 if (!normalizedPath.StartsWith($"{AssetsFolder}/", StringComparison.Ordinal))
363 {
364 throw new UnityException($"Path must be {AssetsFolder}-relative: {assetsRelativePath}");
365 }
366
367 var relativePath = normalizedPath[(AssetsFolder.Length + 1)..];
368 return Path.GetFullPath(Path.Combine(Application.dataPath, relativePath)).Replace('\\', '/');
369 }
370
371 #endregion
372 }
373}