using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using Colossal;
using Colossal.Annotations;
using Colossal.Logging;
using Colossal.UI.Binding;
using Game;
using Game.Common;
using Game.SceneFlow;
using Game.UI;
using HarmonyLib;
using HookUI.UI;
using HookUILib.Core;
using Microsoft.CodeAnalysis;
using UnityEngine;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("HookUIMod")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyDescription("Mod UI loader/framework for Cities: Skylines 2")]
[assembly: AssemblyFileVersion("1.1.0.0")]
[assembly: AssemblyInformationalVersion("1.1.0")]
[assembly: AssemblyProduct("HookUIMod")]
[assembly: AssemblyTitle("HookUIMod")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.1.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
internal sealed class EmbeddedAttribute : Attribute
{
}
}
namespace System.Runtime.CompilerServices
{
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
internal sealed class NullableAttribute : Attribute
{
public readonly byte[] NullableFlags;
public NullableAttribute(byte P_0)
{
NullableFlags = new byte[1] { P_0 };
}
public NullableAttribute(byte[] P_0)
{
NullableFlags = P_0;
}
}
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
internal sealed class NullableContextAttribute : Attribute
{
public readonly byte Flag;
public NullableContextAttribute(byte P_0)
{
Flag = P_0;
}
}
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
internal sealed class RefSafetyRulesAttribute : Attribute
{
public readonly int Version;
public RefSafetyRulesAttribute(int P_0)
{
Version = P_0;
}
}
}
namespace HookUI.UI
{
internal class HookUIUISystem : UISystemBase
{
private HashSet<string> availableExtensions = new HashSet<string>();
private HashSet<string> visiblePanels = new HashSet<string>();
private bool isMenuOpen = false;
private string kGroup = "hookui";
public static ILog Log { get; private set; }
protected override void OnCreate()
{
((UISystemBase)this).OnCreate();
Log = LogManager.GetLogger("HookUIUISystem");
((UISystemBase)this).AddUpdateBinding((IUpdateBinding)(object)new GetterValueBinding<HashSet<string>>(kGroup, "visible_panels", (Func<HashSet<string>>)(() => visiblePanels), (IWriter<HashSet<string>>)new HashSetWriter<string>(), (EqualityComparer<HashSet<string>>)null));
((UISystemBase)this).AddUpdateBinding((IUpdateBinding)(object)new GetterValueBinding<HashSet<string>>(kGroup, "available_extensions", (Func<HashSet<string>>)(() => availableExtensions), (IWriter<HashSet<string>>)new HashSetWriter<string>(), (EqualityComparer<HashSet<string>>)null));
((UISystemBase)this).AddUpdateBinding((IUpdateBinding)(object)new GetterValueBinding<bool>(kGroup, "is_menu_open", (Func<bool>)(() => isMenuOpen), (IWriter<bool>)null, (EqualityComparer<bool>)null));
((UISystemBase)this).AddBinding((IBinding)(object)new TriggerBinding<bool>(kGroup, "toggle_menu", (Action<bool>)delegate(bool is_open)
{
isMenuOpen = is_open;
}, (IReader<bool>)null));
((UISystemBase)this).AddBinding((IBinding)(object)new TriggerBinding<HashSet<string>>(kGroup, "set_visible_panels", (Action<HashSet<string>>)delegate(HashSet<string> panels)
{
visiblePanels = panels;
}, (IReader<HashSet<string>>)new HashSetReader<string>()));
Log.Info((object)"Finding hookui extensions");
Type baseType = typeof(UIExtension);
IEnumerable<Type> enumerable = from type in AppDomain.CurrentDomain.GetAssemblies().SelectMany((Assembly assembly) => assembly.GetTypes())
where type.IsSubclassOf(baseType)
select type;
foreach (Type item in enumerable)
{
object obj = Activator.CreateInstance(item);
string fullName = item.FullName;
object value = item.GetField("extensionID", BindingFlags.Instance | BindingFlags.Public).GetValue(obj);
string text = (string)item.GetField("extensionContent", BindingFlags.Instance | BindingFlags.Public).GetValue(obj);
string text2 = $"panel.{value}.js";
Log.Info((object)$"{fullName} extensionPath: {value}, extensionContent length: {text.Length}");
string text3 = Path.Combine(Application.dataPath, "StreamingAssets", "~UI~", "HookUI", "Extensions", text2);
Log.Info((object)(fullName + " installPath: " + text3));
File.WriteAllText(text3, text);
AddExtension(text2);
}
}
public void AddExtension(string id)
{
availableExtensions.Add(id);
}
}
internal class HashSetWriter<T> : IWriter<HashSet<T>>
{
[NotNull]
private readonly IWriter<T> m_ItemWriter;
public HashSetWriter(IWriter<T> itemWriter = null)
{
m_ItemWriter = itemWriter ?? ValueWriters.Create<T>();
}
public void Write(IJsonWriter writer, HashSet<T> value)
{
if (value != null)
{
JsonWriterExtensions.ArrayBegin(writer, value.Count);
foreach (T item in value)
{
m_ItemWriter.Write(writer, item);
}
writer.ArrayEnd();
return;
}
writer.WriteNull();
throw new ArgumentNullException("value", "Null passed to non-nullable hashset writer");
}
}
internal class HashSetReader<T> : IReader<HashSet<T>>
{
private readonly IReader<T> m_ItemReader;
public HashSetReader(IReader<T> itemReader = null)
{
m_ItemReader = itemReader ?? ValueReaders.Create<T>();
}
public void Read(IJsonReader reader, out HashSet<T> value)
{
value = new HashSet<T>();
uint num = reader.ReadArrayBegin();
T item = default(T);
for (uint num2 = 0u; num2 < num; num2++)
{
reader.ReadArrayElement(num2);
m_ItemReader.Read(reader, ref item);
value.Add(item);
}
reader.ReadArrayEnd();
}
}
public record PanelPosition
{
public int top;
public int left;
}
public record PanelSize
{
public int height;
public int width;
}
}
namespace HookUIMod
{
[BepInPlugin("HookUIMod", "HookUIMod", "1.1.0")]
public class Plugin : BaseUnityPlugin
{
private readonly string hookUIPath = Path.Combine(Application.dataPath, "StreamingAssets", "~UI~", "HookUI");
private readonly string extensionsPath = Path.Combine(Application.dataPath, "StreamingAssets", "~UI~", "HookUI", "Extensions");
private FileSystemWatcher watcher;
private void Awake()
{
//IL_001e: Unknown result type (might be due to invalid IL or missing references)
//IL_0023: Unknown result type (might be due to invalid IL or missing references)
((BaseUnityPlugin)this).Logger.LogInfo((object)"Plugin HookUIMod is loaded!");
string filePath = "Cities2_Data\\StreamingAssets\\~UI~\\HookUI\\index.html.template";
string destinationPath = "Cities2_Data\\StreamingAssets\\~UI~\\HookUI\\index.html";
Version current = Version.current;
string shortVersion = ((Version)(ref current)).shortVersion;
string text = "1.1.0f1";
Harmony val = Harmony.CreateAndPatchAll(Assembly.GetExecutingAssembly(), "HookUIMod_Cities2Harmony");
MethodBase[] array = val.GetPatchedMethods().ToArray();
((BaseUnityPlugin)this).Logger.LogInfo((object)("Plugin HookUIMod made patches! Patched methods: " + array.Length));
MethodBase[] array2 = array;
foreach (MethodBase methodBase in array2)
{
((BaseUnityPlugin)this).Logger.LogInfo((object)("Patched method: " + methodBase.Module.Name + ":" + methodBase.Name));
}
InstallHookUI();
if (CheckVersion(shortVersion, text))
{
WriteFileToPath(filePath, destinationPath);
}
else
{
PrintVersionWarning(filePath, destinationPath, shortVersion, text);
}
}
public static string InjectHookUILoader(string orig_text)
{
string oldValue = "tutorialListFocusKey:pIe.tutorialList})";
string newValue = "tutorialListFocusKey:pIe.tutorialList}),(0,z.jsx)(window._$hookui_loader,{react:Y})";
return orig_text.Replace(oldValue, newValue);
}
public static string HookPanelsMenu(string orig_text)
{
string oldValue = "ube.lock})})})})]";
string newValue = "ube.lock})})})}),(0,z.jsx)(window._$hookui_menu,{react:Y})]";
return orig_text.Replace(oldValue, newValue);
}
public static string OverwriteAbsoluteButton(string orig_text)
{
string oldValue = ".button_H9N{pointer-events:auto;position:absolute;top:0;left:0}";
string newValue = ".button_H9N{pointer-events:auto}";
return orig_text.Replace(oldValue, newValue);
}
public static void WriteResourcesToDisk()
{
string text = "Cities2_Data\\StreamingAssets\\~UI~\\HookUI\\lib";
string[] array = new string[3] { "hookui.api.bundle.js", "hookui.loader.bundle.js", "hookui.menu.bundle.js" };
Assembly executingAssembly = Assembly.GetExecutingAssembly();
Directory.CreateDirectory(text);
string[] array2 = array;
foreach (string text2 in array2)
{
string name = "HookUIMod." + text2;
using Stream stream = executingAssembly.GetManifestResourceStream(name);
if (stream == null)
{
throw new InvalidOperationException("Could not find embedded resource: " + text2);
}
string path = Path.Combine(text, text2);
using FileStream destination = new FileStream(path, FileMode.Create);
stream.CopyTo(destination);
}
}
public static void InstallHookUI()
{
string text = "Cities2_Data\\StreamingAssets\\~UI~";
CopyDirectory(text + "\\GameUI", text + "\\HookUI");
File.WriteAllText(text + "\\HookUI\\version", "1.1.0");
string path = text + "\\HookUI\\index.js";
string orig_text = File.ReadAllText(path);
orig_text = InjectHookUILoader(orig_text);
orig_text = HookPanelsMenu(orig_text);
File.WriteAllText(path, orig_text);
string path2 = text + "\\HookUI\\index.css";
string orig_text2 = File.ReadAllText(path2);
orig_text2 = OverwriteAbsoluteButton(orig_text2);
File.WriteAllText(path2, orig_text2);
WriteResourcesToDisk();
Directory.CreateDirectory(text + "\\HookUI\\Extensions");
string name = "HookUIMod.index.html.template";
string path3 = Path.Combine("Cities2_Data\\StreamingAssets\\~UI~\\HookUI", "index.html.template");
using Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(name);
using FileStream destination = new FileStream(path3, FileMode.Create, FileAccess.Write);
stream.CopyTo(destination);
}
public static bool CheckVersion(string currentVersion, string compatibleVersion)
{
Debug.Log((object)"HookUI VersionCheck");
Debug.Log((object)currentVersion);
if (currentVersion == compatibleVersion)
{
Debug.Log((object)"Passed version check");
string text = "";
string text2 = "";
string text3 = "CAA2852C609B391E942A474EA4A26A4AD14E66DE6A1C5FEE4E1B8C111D3E9492";
string text4 = "CAA2852C609B391E942A474EA4A26A4AD14E66DE6A1C5FEE4E1B8C111D3E9492";
bool flag = true;
bool flag2 = true;
Debug.Log((object)"Passed hash checks");
return true;
}
Debug.Log((object)"This HookUI version might not be compatible with your game version");
return false;
}
public static void CopyDirectory(string sourceDir, string destinationDir)
{
DirectoryInfo directoryInfo = new DirectoryInfo(destinationDir);
if (!directoryInfo.Exists)
{
directoryInfo.Create();
}
DirectoryInfo directoryInfo2 = new DirectoryInfo(sourceDir);
FileInfo[] files = directoryInfo2.GetFiles();
foreach (FileInfo fileInfo in files)
{
string destFileName = Path.Combine(destinationDir, fileInfo.Name);
fileInfo.CopyTo(destFileName, overwrite: true);
}
DirectoryInfo[] directories = directoryInfo2.GetDirectories();
foreach (DirectoryInfo directoryInfo3 in directories)
{
string destinationDir2 = Path.Combine(destinationDir, directoryInfo3.Name);
CopyDirectory(directoryInfo3.FullName, destinationDir2);
}
}
public static void WriteFileToPath(string filePath, string destinationPath)
{
string contents = File.ReadAllText(filePath);
File.WriteAllText(destinationPath, contents);
}
public static void PrintVersionWarning(string filePath, string destinationPath, string actualVersion, string expectedVersion)
{
string text = File.ReadAllText(filePath);
text = text.Replace("<!--EXTENSIONS_LIST-->", "<div style=\"position: absolute; top: 10px; left: 10px; z-index: 10000; color: white;\" onclick=\"if (this.parentNode) this.parentNode.removeChild(this);\"><div>This HookUI version is not compatible with your game version.</div><div>Loading of extensions disabled.</div><div>" + actualVersion + " = Your CS2 version</div><div>" + expectedVersion + " = Last tested CS2 version with HookUI</div><div>Click to hide</div></div>");
File.WriteAllText(destinationPath, text);
}
public List<string> GenerateScriptPathsList(string directoryPath)
{
string[] files = Directory.GetFiles(directoryPath, "*.js");
return files.Select((string fullPath) => "Extensions/" + Path.GetFileName(fullPath)).ToList();
}
}
public static class MyPluginInfo
{
public const string PLUGIN_GUID = "HookUIMod";
public const string PLUGIN_NAME = "HookUIMod";
public const string PLUGIN_VERSION = "1.1.0";
}
}
namespace HookUIMod.Patches
{
[HarmonyPatch(typeof(SystemOrder))]
public static class SystemOrderPatch
{
[HarmonyPatch("Initialize")]
[HarmonyPostfix]
public static void Postfix(UpdateSystem updateSystem)
{
updateSystem.UpdateAt<HookUIUISystem>((SystemUpdatePhase)22);
}
}
[HarmonyPatch(typeof(GameManager))]
[HarmonyPatch("InitializeUI")]
public static class GameManager_InitializeUIPatch
{
public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
{
List<CodeInstruction> list = new List<CodeInstruction>(instructions);
string text = "/~UI~/GameUI";
string text2 = "/~UI~/HookUI";
for (int i = 0; i < list.Count; i++)
{
if (list[i].opcode == OpCodes.Ldstr && (string)list[i].operand == text)
{
Debug.Log((object)$"Replacing string: {list[i].operand} with {text2}");
list[i].operand = text2;
}
}
return list.AsEnumerable();
}
}
[HarmonyPatch(typeof(UISystemBootstrapper))]
[HarmonyPatch("Awake")]
public static class UISystemBootstrapper_AwakePatch
{
public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
{
List<CodeInstruction> list = new List<CodeInstruction>(instructions);
string text = "/~UI~/GameUI";
string text2 = "/~UI~/HookUI";
for (int i = 0; i < list.Count; i++)
{
if (list[i].opcode == OpCodes.Ldstr && (string)list[i].operand == text)
{
Debug.Log((object)$"Replacing string: {list[i].operand} with {text2}");
list[i].operand = text2;
}
}
return list.AsEnumerable();
}
}
}