// Undead Universal Patch - for patching Rec Room builds // Copyright (C) 2026 proxnet.dev (@zombieb) // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY using HarmonyLib; using System; using System.Linq; using System.Reflection; using UndeadUniversalPatch.Core.Exceptions; namespace UndeadUniversalPatch.Core; internal static class BuildSettings { public static Assembly GetAssemblyCSharp() { if (!Init.GetIsRecRoom()) throw new NotRecRoomException(); return AccessTools.TypeByName("BootSequence").Assembly; // always unobfuscated } private static Type GetBuildSettings() { var assembly = GetAssemblyCSharp(); try { Type type = assembly.GetTypes().First(type => Symbols.BuildSettings.Contains(type.Name)); return type; } catch (ReflectionTypeLoadException ex) { Type type = ex.Types.First(type => Symbols.BuildSettings.Contains(type.Name)); return type; } catch { return null; } } public static string GetVersion() { var unknown = "Unknown Version"; var buildSettings = GetBuildSettings(); if (buildSettings == null) #if NET35 { try { // doesn't exist? -> error will be caught & unknown will be returned var field = AccessTools.Field("PUNNetworkManager:BUILD_VERSION"); if (field != null) return (string)field.GetValue(null); else return "Unknown Legacy Version"; } catch { return unknown; } } #else return unknown; #endif Func check = (string value) => { if (value == null) return false; return value.StartsWith("20") || value.EndsWith("_EA"); }; try { #if NET6_0 return (string)buildSettings.GetRuntimeProperties().Where(prop => prop.PropertyType == typeof(string)).First(prop => { var value = (string)prop.GetValue(null); return check(value); }).GetValue(null); #else return (string)buildSettings.GetFields().Where(field => field.FieldType == typeof(string)).First(field => { var value = (string)field.GetValue(null); return check(value); }).GetValue(null); #endif } catch { return unknown; } } public static string GetBuildTimestamp() { var unknown = "Unknown Timestamp"; var buildSettings = GetBuildSettings(); if (buildSettings == null) return unknown; // Only one `long` exists on BuildSettings across all supported builds try { #if NET6_0 var foundProp = buildSettings.GetRuntimeProperties() .First(prop => prop.PropertyType == typeof(long)); var value = (long)foundProp.GetValue(null); return new DateTime(value).ToString(); #else var foundField = buildSettings.GetFields() .First(field => field.FieldType == typeof(long)); var value = (long)foundField.GetValue(null); return new DateTime(value).ToString(); #endif } catch { return unknown; } } private static class Symbols { // type list compiled by @zombieb public static string[] BuildSettings = [ "BuildSettings", "BBHENFCNLAB", "KMJJGELKNAP", "FJEEPOKGDNG", "LACHJMJGMPK", "NNBKABHFGFB", "KMGJBIHAAJL", "FALNDOJFPMN", "IODMKGDNMID", "HIPJBMLBLHH", "LEADBBLICBL", "NPPACKKFLIK", "MOFAAFLMFJO", "EMIDOAHOJNO", "IKJNDDBHIAC", "OOCIKEIJMCA", "HPFCOJFIIKK", "MONBJIBCJFA", "GFDKEKGOMCP", "MIMHAGFDGND", "LFJMHJPHKOK", "NOHBMMJBMIP", "GJBEJNGDEJM", "OGBGLFJGKJL", "PEJOGOOEOKE", "FDKFNHNNDEE", "EDNIOJBEKLL", "FEKFIFPGDDP", "POOFEABAPOB", "JHFKADKFKPD", "GGKABOEOKMI", "FAPDBIGGJHM", "EPKEIOCFFMO", "ECLPNEHKLEP", "BGMDPOMLCKD", "IMJIAGILBOG", "OGGKBKOBHLP", "DMAKDOEIOFE", "HHGCEDNGMDD", "OFDPCKGMCFG", "FPABNHKCIDD", "NEOOKMCECAL", "CKEBMNKBEAH", "IBPMLICGIOI", "LMJOEPJHBLC", "HJINOEPLIMF", "JMHFJIBKCMG", "HJKAEMGCIGB", "BNOEINBHPIG", "AJJAIFIKCJP", "FCPHGJMAGPI", "DDCENHLJGNM", "AEPEEKCCDJL", "IDDIAODMAPP", "CHIMGKLHFMK", "OGDOAJJPBOC", "EMABNNHNJIB", "JNEAMPKAHFM", "FPGILFHDJNI", "MDPMNBEPOKF", "FNKGHFILBEE", "AEOMDHAKPGC", "OGEBJBBMDEO", "CJMCNIDOMCP", "LCCKMCOLKFI", "DEIHAEKLPDJ", "LENMMNLPJOL", "JKNIDJECBFD", "FMEMABGECKK", "HAOBGEPJMAN", "FDCGGKFBOPN", "OKCPMCEGCOK", "BNKCNOFCGDK" ]; } }